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 相关文章推荐
IE本地存储userdata的一个bug说明
Jul 01 Javascript
用jquery中插件dialog实现弹框效果实例代码
Nov 15 Javascript
JQUERY dialog的用法详细解析
Dec 19 Javascript
js密码强度校验
Nov 10 Javascript
hovertree插件实现二级树形菜单(简单实用)
Dec 28 Javascript
JQuery 封装 Ajax 常用方法(推荐)
May 21 jQuery
Node.js中环境变量process.env的一些事详解
Oct 26 Javascript
通过vue-router懒加载解决首次加载时资源过多导致的速度缓慢问题
Apr 08 Javascript
详解如何在vscode里面调试js和node.js的方法步骤
Dec 24 Javascript
d3.js 地铁轨道交通项目实战
Nov 27 Javascript
node.js开发辅助工具nodemon安装与配置详解
Feb 06 Javascript
video.js添加自定义组件的方法
Dec 09 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/03 咖啡文化
利用js调用后台php进行数据处理原码
2006/10/09 PHP
谈谈新手如何学习PHP
2006/12/23 PHP
PHP error_log()将错误信息写入一个文件(定义和用法)
2013/10/25 PHP
PHP开发工具ZendStudio下Xdebug工具使用说明详解
2013/11/11 PHP
php生成excel列序号代码实例
2013/12/24 PHP
PHP魔术方法__GET、__SET使用实例
2014/11/25 PHP
php模拟服务器实现autoindex效果的方法
2015/03/10 PHP
php定义一个参数带有默认值的函数实例分析
2015/03/16 PHP
简单的php+mysql聊天室实现方法(附源码)
2016/01/05 PHP
juqery 学习之三 选择器 简单 内容
2010/11/25 Javascript
javascript自执行函数之伪命名空间封装法
2010/12/25 Javascript
js实现的切换面板实例代码
2013/06/17 Javascript
jQuery检测输入的字符串包含的中英文的数量
2015/04/17 Javascript
Javascript复制实例详解
2016/01/28 Javascript
JS判断日期格式是否合法的简单实例
2016/07/11 Javascript
vue环境搭建简单教程
2017/11/07 Javascript
vue-cli项目中使用echarts图表实例
2018/10/22 Javascript
详解ES6数组方法find()、findIndex()的总结
2020/05/12 Javascript
[03:49]显微镜下的DOTA2第十五期—VG登基之路完美团
2014/06/24 DOTA
python使用ctypes模块调用windowsapi获取系统版本示例
2014/04/17 Python
简单介绍Python中的RSS处理
2015/04/13 Python
python实现复制整个目录的方法
2015/05/12 Python
Python中创建字典的几种方法总结(推荐)
2017/04/27 Python
Python实现采用进度条实时显示处理进度的方法
2017/12/19 Python
python实现关闭第三方窗口的方法
2019/06/28 Python
python 获取等间隔的数组实例
2019/07/04 Python
Python qqbot 实现qq机器人的示例代码
2019/07/11 Python
Python使用sys.exc_info()方法获取异常信息
2020/07/23 Python
Spartoo美国:欧洲排名第一的在线时装零售商
2019/12/12 全球购物
建筑系毕业生自我鉴定
2014/01/24 职场文书
预备党员表决心书
2014/03/11 职场文书
反邪教标语
2014/06/23 职场文书
新郎答谢词
2015/01/04 职场文书
新郎结婚保证书
2015/02/26 职场文书
2015小学师德工作总结
2015/07/21 职场文书