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 相关文章推荐
静态的动态续篇之来点XML
Dec 23 Javascript
学习ExtJS 访问容器对象
Oct 07 Javascript
基于Asp.net与Javascript控制的日期控件
May 22 Javascript
jquery获得option的值和对option进行操作
Dec 13 Javascript
javascript中对Date类型的常用操作小结
May 19 Javascript
jQuery Easyui学习教程之实现datagrid在没有数据时显示相关提示内容
Jul 09 Javascript
jQuery zTree树插件动态加载实例代码
May 11 jQuery
babel之配置文件.babelrc入门详解
Feb 22 Javascript
vue的列表交错过渡实现代码示例
May 05 Javascript
如何在postman测试用例中实现断言过程解析
Jul 09 Javascript
在vue中使用inheritAttrs实现组件的扩展性介绍
Dec 07 Vue.js
详解JavaScript中分解数字的三种方法
Jan 05 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实现在线阅读PDF文件的方法
2015/06/23 PHP
制作个性化的WordPress登陆界面的实例教程
2016/05/21 PHP
PHP获取二叉树镜像的方法
2018/01/17 PHP
php微信支付之公众号支付功能
2018/05/30 PHP
phpinfo无法显示的原因及解决办法
2019/02/15 PHP
ArrayList类(增强版)
2007/04/04 Javascript
让textarea控件的滚动条怎是位与最下方
2007/04/20 Javascript
javascript fullscreen全屏实现代码
2009/04/09 Javascript
ASP.NET jQuery 实例18 通过使用jQuery validation插件校验DropDownList
2012/02/03 Javascript
jquery 多行文本框(textarea)高度变化
2013/07/03 Javascript
JavaScript人脸识别技术及脸部识别JavaScript类库Tracking.js
2015/09/14 Javascript
javascript实现随机显示星星特效
2016/01/28 Javascript
JS深度拷贝Object Array实例分析
2016/03/31 Javascript
AngularJS报错$apply already in progress的解决方法分析
2017/01/30 Javascript
AngularJS学习第二篇 AngularJS依赖注入
2017/02/13 Javascript
three.js实现3D模型展示的示例代码
2017/12/31 Javascript
Vue快速实现通用表单验证的示例代码
2020/01/09 Javascript
基于Echarts图表在div动态切换时不显示的解决方式
2020/07/20 Javascript
[09:47]2018DOTA2亚洲邀请赛4.5SOLO赛 No[o]ne vs Sumail
2018/04/06 DOTA
[42:25]EG vs Spirit Supermajor 败者组 BO3 第二场 6.4
2018/06/05 DOTA
Python爬取十篇新闻统计TF-IDF
2018/01/03 Python
python生成1行四列全2矩阵的方法
2018/08/04 Python
解决python opencv无法显示图片的问题
2018/10/28 Python
python的debug实用工具 pdb详解
2019/07/12 Python
python随机数分布random均匀分布实例
2019/11/27 Python
基于django 的orm中非主键自增的实现方式
2020/05/18 Python
简单介绍一下pyinstaller打包以及安全性的实现
2020/06/02 Python
Python jieba结巴分词原理及用法解析
2020/11/05 Python
用CSS禁用输入法(CSS3 UI规范)实例解析
2012/12/04 HTML / CSS
将n个数按输入顺序的逆序排列,用函数实现
2012/11/14 面试题
警察群众路线整改措施
2014/09/26 职场文书
税务会计岗位职责
2015/04/02 职场文书
冬季作息时间调整通知
2015/04/24 职场文书
MySQL派生表联表查询实战过程
2022/03/20 MySQL
HTML静态页面获取url参数和UserAgent的实现
2022/08/05 HTML / CSS
MySQL深分页问题解决思路
2022/12/24 MySQL