React.js组件实现拖拽排序组件功能过程解析


Posted in Javascript onApril 27, 2020

因为使用了react.js技术栈,所以封装优先考虑输入和输出。基于数据驱动去渲染页面、控制拖拽元素的顺序。

由于我不考虑兼容IE8等旧版本浏览器,拖拽的效果采用了HTML5的拖放(Drag 和 drop)。当然,如果要求兼容性丰富,使用鼠标点击的相关事件也很简单。

实现的效果如下:

React.js组件实现拖拽排序组件功能过程解析

第一步是先了解H5拖放的相关属性,MDN上有详细的说明,链接

有一点需要注意的是,react.js会给所有的属性事件名称前加上"on",后面则为驼峰式写法。例如原生的click事件,在react.js里应使用onClick事件。

我的组件使用的拖放属性如下:

  • draggable 当设置为true时,当前控件可以拖拽
  • onDragStart 控件开始被拖拽时触发的事件,它提供一个dataTransfer.setData()方法,将必要的数据存储在对象中便于在其它方法中调用
  • onDragOver 规定当前控件可以接收拖拽的组件的方法,一般在此方法中阻止冒泡
  • onDragEnter 拖动后鼠标进入另一个可接受区域时触发,通过它可以实现移入效果
  • onDragLeave a拖到b,离开b的时候触发,可以用于监听消除移入效果的时机
  • onDrop 当控件被“释放”到一个有效的释放目标位置时触发,我在这个方法中处理数据,并通过它调用onChange方法,将value值暴露给父组件

其中draggable,onDragStart是被“拖拽”方需要设置的属性,onDragOver,onDragEnter,onDragLeave和onDrop是被“拖入”方需要设置的属性。不过对于我的拖拽排序组件,每一个元素都是拖拽和拖入方

第二步,既然“她"是react.js的组件, 按照习惯,简单的将输入属性定为为value,同时,暴露onChange事件监听value的变化,并将其暴露给父组件,同时,暴露一个属性sortKey告诉组件使用哪个key作为排序字段。
既然涉及到排序,同时允许指定组件每个元素的内部子组件,我将输入数据格式定义为一个数组对象,其中content可以为reactNode:

value: [
    {
     content: 'div1',
     code: '01',
     sort: 0,
    },
    {
     content: 'div2',
     code: '02',
     sort: 1
    },
    {
     content: 'div3',
     code: '03',
     sort: 2
    },
    {
     content: 'div5',
     code: '05',
     sort: 5
    },
    {
     content: 'div4',
     code: '04',
     sort: 4
    }]

根据value我去生成可排序组件的每个node,关键代码如下:

// 生成拖拽组件
 createDraggleComponent(data, sortKey, style, uId) {
  return data.sort(this.compare(sortKey)).map((item) => {
   return (
    <div
     className={styles.content}
     key={item.code}
     draggable={true}
     onDragEnter={this.dragenter.bind(this)}
     onDragLeave={this.dragleave.bind(this)}
     onDragStart={this.domdrugstart.bind(this, item[sortKey], item.code, uId, item)}
     onDrop={this.drop.bind(this, item[sortKey], data, sortKey, uId)}
     onDragOver={this.allowDrop.bind(this)}
     style={{ ...style }}>{item.content}</div>
   )
  })
 }
 render() {
  const { value, sortKey, style } = this.props;
  return (
   <Row>
    <div style={{ display: 'flex', flexDirection: 'row' }}>
     {this.createDraggleComponent(value, sortKey, style)}
    </div>
   </Row>
  )
 }

其中的属性方法具体实现:

// 拖动事件
 domdrugstart(sort, code, ee) {
  ee.dataTransfer.setData("code", code);
  ee.dataTransfer.setData("sort", sort);
 }
 // 拖动后鼠标进入另一个可接受区域
 dragenter(ee) {
  ee.target.style.border = '2px dashed #008dff';
  ee.target.style.boxShadow = '0 0 8px rgba(30, 144, 255, 0.8)';
 }
 // a拖到b,离开b的时候触发
 dragleave(ee) {
  ee.target.style.border = '1px solid grey';
  ee.target.style.boxShadow = '';
 }
 // 对象排序
 compare(key) {
  return (obj1, obj2) => {
   if (obj1[key] < obj2[key]) {
    return -1;
   } else if (obj1[key] > obj2[key]) {
    return 1;
   }
   return 0
  }
 }
 // 当一个元素或是选中的文字被拖拽释放到一个有效的释放目标位置时
 drop(dropedSort, data, sortKey, ee) {
  ee.preventDefault();
  const code = ee.dataTransfer.getData("code");
  const sort = ee.dataTransfer.getData("sort");
  if (sort < dropedSort) {
   data.map(item => {
    if (item.code === code) {
     item[sortKey] = dropedSort;
    } else if (item[sortKey] > sort && item[sortKey] < dropedSort + 1) {
     item[sortKey]--;
    }
    return item;
   });
  } else {
   data.map(item => {
    if (item.code === code) {
     item[sortKey] = dropedSort;
    } else if (item[sortKey] > dropedSort - 1 && item[sortKey] < sort) {
     item[sortKey]++;
    }
    return item;
   });
  }
  this.props.onChange(data)
 }
 allowDrop(ee) {
  ee.preventDefault();
 }

值得注意的点其实只有一个,我控制顺序的时候,并没有使用.target.before(document.getElementById({id}))去实际操控节点,而是在每次触发onDrop时间的时候,处理数据的sort,并通过onChange事件暴露给父组件,将数据输出,通过改变value值触发虚拟dom重新去渲染,以此控制顺序。

根据公司的要求,在此基础上,我还实现了拖拽复制的功能,这个等下次自己不懒的时候再记录下来。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js中几种去掉字串左右空格的方法
Dec 25 Javascript
jquery属性选择器not has怎么写 行悬停高亮显示
Nov 13 Javascript
js判断文本框剩余可输入字数的方法
Feb 04 Javascript
js+html5实现canvas绘制简单矩形的方法
Jun 05 Javascript
jquery实现select选择框内容左右移动代码分享
Nov 21 Javascript
微信小程序 require机制详解及实例代码
Dec 14 Javascript
Vue 2.X的状态管理vuex记录详解
Mar 23 Javascript
jQuery实现web页面樱花坠落的特效
Jun 01 jQuery
React BootStrap用户体验框架快速上手
Mar 06 Javascript
p5.js 毕达哥拉斯树的实现代码
Mar 23 Javascript
layui.tree组件的使用以及搜索节点功能的实现
Sep 26 Javascript
使用Bootstrap做一个朝代历史表
Dec 10 Javascript
element 中 el-menu 组件的无限极循环思路代码详解
Apr 26 #Javascript
微信小程序个人中心的列表控件实现代码
Apr 26 #Javascript
vue项目中自定义video视频控制条的实现代码
Apr 26 #Javascript
vue项目启动出现cannot GET /服务错误的解决方法
Apr 26 #Javascript
详解vuejs中执行npm run dev出现页面cannot GET/问题
Apr 26 #Javascript
jquery检测上传文件大小示例
Apr 26 #jQuery
element中的$confirm的使用
Apr 26 #Javascript
You might like
短波的认识
2021/03/01 无线电
模板引擎Smarty深入浅出介绍
2006/12/06 PHP
兼容PHP5的PHP目录管理函数库
2008/07/10 PHP
攻克CakePHP系列二 表单数据显示
2008/10/22 PHP
处理php自动反斜杠的函数代码
2010/01/05 PHP
PHP统计目录下的文件总数及代码行数(去除注释及空行)
2011/01/17 PHP
jquery的颜色选择插件实例代码
2008/10/02 Javascript
jquery网页元素拖拽插件效果及实现
2013/08/05 Javascript
Javascript中的call()方法介绍
2015/03/15 Javascript
JS实现可直接显示网页代码运行效果的HTML代码预览功能实例
2015/08/06 Javascript
AngularJS基础 ng-show 指令简单示例
2016/08/03 Javascript
浅析如何利用JavaScript进行语音识别
2016/10/27 Javascript
JS中用三种方式实现导航菜单中的二级下拉菜单
2016/10/31 Javascript
vue打包使用Nginx代理解决跨域问题
2018/08/27 Javascript
每周一练 之 数据结构与算法(Stack)
2019/04/16 Javascript
使用Layer组件弹出多个对话框(非嵌套)与关闭及刷新的例子
2019/09/25 Javascript
vue.js路由mode配置之去掉url上默认的#方法
2019/11/01 Javascript
微信小程序实现时间进度条功能
2020/11/17 Javascript
vue项目使用高德地图的定位及关键字搜索功能的实例代码(踩坑经验)
2020/03/07 Javascript
Python使用htpasswd实现基本认证授权的例子
2014/06/10 Python
Python中的左斜杠、右斜杠(正斜杠和反斜杠)
2016/08/30 Python
Python 绘图库 Matplotlib 入门教程
2018/04/19 Python
python操作excel的包(openpyxl、xlsxwriter)
2018/06/11 Python
django rest framework vue 实现用户登录详解
2019/07/29 Python
canvas之自定义头像功能实现代码示例
2017/09/29 HTML / CSS
Raffaello Network西班牙:意大利拉斐尔时尚购物网
2019/03/12 全球购物
美国职棒大联盟的官方手套、球和头盔:Rawlings
2020/02/15 全球购物
客服工作职责
2013/12/11 职场文书
应用英语专业自荐信
2014/01/26 职场文书
骨干教师事迹材料
2014/12/17 职场文书
校长师德表现自我评价
2015/03/04 职场文书
2015年会计个人工作总结
2015/04/02 职场文书
写给女朋友的保证书
2015/05/09 职场文书
MySql存储过程之逻辑判断和条件控制
2021/05/26 MySQL
Windows server 2012搭建FTP服务器
2022/04/29 Servers
Springboot中如何自动转JSON输出
2022/06/16 Java/Android