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 相关文章推荐
将string解析为json的几种方式小结
Nov 11 Javascript
JS调试必备的5个debug技巧
Mar 07 Javascript
jQuery中$.each使用详解
Jan 29 Javascript
javascript实现的字符串与十六进制表示字符串相互转换方法
Jul 17 Javascript
js实现索引图片切换效果
Nov 21 Javascript
基于JavaScript实现类似于百度学术高级检索功能
Mar 02 Javascript
jquery实现图片上传前本地预览
Apr 28 jQuery
JS求Number类型数组中最大元素方法
Apr 08 Javascript
通过实践编写优雅的JavaScript代码
May 30 Javascript
Koa从零搭建到Api实现项目的搭建方法
Jul 30 Javascript
详解基于Wepy开发小程序插件(推荐)
Aug 01 Javascript
如何使JavaScript休眠或等待
Apr 27 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
PHP类中Static方法效率测试代码
2010/10/17 PHP
三个类概括PHP的五种设计模式
2012/09/05 PHP
php类声明和php类使用方法示例分享
2014/03/29 PHP
PHP使用array_fill定义多维数组的方法
2015/03/18 PHP
IIS 7.5 asp Session超时时间设置方法
2017/04/17 PHP
Yii2压缩PHP中模板代码的输出问题
2018/08/28 PHP
PHP常用函数之base64图片上传功能详解
2019/10/21 PHP
javascript编程起步(第三课)
2007/02/27 Javascript
javascript解决IE6下hover问题的方法
2015/07/28 Javascript
JavaScript知识点总结之如何提高性能
2016/01/15 Javascript
Node.js的项目构建工具Grunt的安装与配置教程
2016/05/12 Javascript
javascript 广告移动特效的实现代码
2016/06/25 Javascript
AngularJS中$http服务常用的应用及参数
2016/08/22 Javascript
jQuery实现的自适应焦点图效果完整实例
2016/08/24 Javascript
深入理解JavaScript中的尾调用(Tail Call)
2017/02/07 Javascript
微信小程序 五星评分的实现实例
2017/08/04 Javascript
Vuejs实现购物车功能
2017/11/05 Javascript
webpack 4.0.0-beta.0版本新特性介绍
2018/02/10 Javascript
layui 上传文件_批量导入数据UI的方法
2019/09/23 Javascript
vue设置动态请求地址的例子
2019/11/01 Javascript
js动态添加带圆圈序号列表的实例代码
2021/02/18 Javascript
[01:32]TI奖金增速竟因它再创新高!DOTA2勇士令状不朽珍藏Ⅰ饰品欣赏
2018/05/18 DOTA
Python文件夹与文件的操作实现代码
2014/07/13 Python
python比较两个列表大小的方法
2015/07/11 Python
使用Python保存网页上的图片或者保存页面为截图
2016/03/05 Python
Python 复平面绘图实例
2019/11/21 Python
荷兰手表网站:Watch2Day
2018/07/02 全球购物
2019年Java面试必问之经典试题
2012/09/12 面试题
岗位说明书范文
2014/05/07 职场文书
单位委托书范本(3篇)
2014/09/18 职场文书
公司财务会计主管应聘求职信
2014/09/26 职场文书
论群众路线学习心得体会
2014/10/31 职场文书
青年岗位能手事迹材料(2016推荐版)
2016/03/01 职场文书
2019企业给员工的慰问信
2019/06/24 职场文书
python入门学习关于for else的特殊特性讲解
2021/11/20 Python
bootstrapv4轮播图去除两侧阴影及线框的方法
2022/02/15 HTML / CSS