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 相关文章推荐
javascript instanceof 内部机制探析
Oct 15 Javascript
借助script进行Http跨域请求:JSONP实现原理及代码
Mar 19 Javascript
往光标所在位置插入值的js代码
Sep 22 Javascript
超赞的jQuery图片滑块动画特效代码汇总
Jan 25 Javascript
基于jQuery实现仿百度首页选项卡切换效果
May 29 Javascript
浅谈javascript中的 “ &amp;&amp; ” 和 “ || ”
Feb 02 Javascript
vue实现点击图片放大效果
Aug 15 Javascript
Vue打包后出现一些map文件的解决方法
Feb 13 Javascript
vue.js的computed,filter,get,set的用法及区别详解
Mar 08 Javascript
vue 组件使用中的一些细节点
Apr 25 Javascript
Vue.js 实现微信公众号菜单编辑器功能(一)
May 08 Javascript
使用vue2.6实现抖音【时间轮盘】屏保效果附源码
Apr 24 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
vBulletin HACK----关于排版的两个HACK
2006/10/09 PHP
[转帖]PHP世纪万年历
2006/12/06 PHP
PHP COOKIE及时生效的方法介绍
2014/02/14 PHP
php上传图片之时间戳命名(保存路径)
2014/08/15 PHP
PHP异常处理Exception类
2015/12/11 PHP
利用php输出不同的心形图案
2016/04/22 PHP
JSON字符串传到后台PHP处理问题的解决方法
2016/06/05 PHP
smarty模板的使用方法实例分析
2019/09/18 PHP
PhpStorm的使用教程(本地运行PHP+远程开发+快捷键)
2020/03/26 PHP
基于jquery的表格排序
2010/09/11 Javascript
IE6下出现JavaScript未结束的字符串常量错误的解决方法
2010/11/21 Javascript
文本框中禁止非数字字符输入比如手机号码、邮编
2013/08/19 Javascript
JS获取html对象的几种方式介绍
2013/12/05 Javascript
javascript打开word文档的方法
2014/04/16 Javascript
基于dropdown.js实现的两款美观大气的二级导航菜单
2015/09/02 Javascript
JavaScript地理位置信息API
2016/06/11 Javascript
vue2.0实战之使用vue-cli搭建项目(2)
2017/03/27 Javascript
jQuery实现百度登录框的动态切换效果
2017/04/21 jQuery
D3.js实现简洁实用的动态仪表盘的示例
2018/04/04 Javascript
Element-UI Table组件上添加列拖拽效果实现方法
2018/04/14 Javascript
巧妙运用v-model实现父子组件传值的方法示例
2019/04/07 Javascript
解决vue-router 二级导航默认选中某一选项的问题
2019/11/01 Javascript
antdesign-vue结合sortablejs实现两个table相互拖拽排序功能
2021/01/08 Vue.js
python简单实现获取当前时间
2016/08/27 Python
使用Pyinstaller的最新踩坑实战记录
2017/11/08 Python
python3 requests中使用ip代理池随机生成ip的实例
2018/05/07 Python
详解如何用python实现一个简单下载器的服务端和客户端
2019/10/28 Python
解决python 读取 log日志的编码问题
2019/12/24 Python
高中毕业自我鉴定范文
2013/10/02 职场文书
美术国培研修感言
2014/02/12 职场文书
基层党支部公开承诺书
2014/05/29 职场文书
2014年单位法制宣传日活动总结
2014/11/01 职场文书
2015年妇联工作总结范文
2015/04/22 职场文书
Python趣味挑战之用pygame实现简单的金币旋转效果
2021/05/31 Python
使用 Apache Dubbo 实现远程通信(微服务架构)
2022/02/12 Servers
python文件与路径操作神器 pathlib
2022/04/01 Python