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 相关文章推荐
对textarea框的代码调试,而且功能上使用非常方便,酷
Jun 30 Javascript
javascript获取元素CSS样式代码示例
Nov 28 Javascript
js判断当前浏览器类型,判断IE浏览器方法
Jun 02 Javascript
JS实现简洁、全兼容的拖动层实例
May 13 Javascript
巧用Javascript的逻辑运算符
Dec 02 Javascript
EasyUI中的dataGrid的行内编辑
Jun 22 Javascript
JS与CSS3实现图片响应鼠标移动放大效果示例
May 04 Javascript
详解node.js的http模块实例演示
Jul 12 Javascript
KOA+egg.js集成kafka消息队列的示例
Nov 09 Javascript
JavaScript的查询机制LHS和RHS解析
Aug 16 Javascript
vue+axios 拦截器实现统一token的案例
Sep 11 Javascript
Vue Element UI自定义描述列表组件
May 18 Vue.js
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中CI操作多个数据库的代码
2012/07/05 PHP
ThinkPHP查询返回简单字段数组的方法
2014/08/25 PHP
在Nginx上部署ThinkPHP项目教程
2015/02/02 PHP
php实现指定字符串中查找子字符串的方法
2015/03/17 PHP
织梦sitemap地图实时推送给百度的教程
2015/08/03 PHP
linux下php上传文件注意事项
2016/06/11 PHP
Yii基于CActiveForm的Ajax数据验证用法示例
2016/07/14 PHP
弹出模态框modal的实现方法及实例
2017/09/19 PHP
详解PHP实现支付宝小程序用户授权的工具类
2018/12/25 PHP
用javascript连接access数据库的方法
2006/11/17 Javascript
css把超出的部分显示为省略号的方法兼容火狐
2008/07/23 Javascript
JQUERY操作JSON实例代码
2010/02/09 Javascript
能说明你的Javascript技术很烂的五个原因分析
2011/10/28 Javascript
高效率JavaScript编写技巧整理
2013/08/23 Javascript
$.each遍历对象、数组的属性值并进行处理
2014/07/18 Javascript
原生javascript实现图片弹窗交互效果
2015/01/12 Javascript
jquery+css实现简单的图片轮播效果
2017/08/07 jQuery
js阻止默认右键的下拉菜单方法
2018/01/02 Javascript
使用mock.js随机数据和使用express输出json接口的实现方法
2018/01/07 Javascript
jquery实现聊天机器人
2020/02/08 jQuery
[42:27]DOTA2上海特级锦标赛主赛事日 - 3 败者组第三轮#2Fnatic VS OG第三局
2016/03/05 DOTA
Python求解平方根的方法
2015/03/11 Python
Sublime开发python程序的示例代码
2018/01/24 Python
pandas 选择某几列的方法
2018/07/03 Python
关于tf.nn.dynamic_rnn返回值详解
2020/01/20 Python
传统HTML页面实现模块化加载的方法
2018/10/15 HTML / CSS
您的健身减肥和健康饮食专家:vitafy
2017/06/06 全球购物
自荐信不宜过于夸大
2013/11/06 职场文书
体育专业求职信
2014/07/16 职场文书
安全生产月标语
2014/10/07 职场文书
公司内部升职自荐信
2015/03/27 职场文书
单位考核鉴定意见
2015/06/05 职场文书
小学毕业感言200字
2015/07/30 职场文书
经典祝酒词大全
2015/08/12 职场文书
小学一年级班主任工作经验交流材料
2015/11/02 职场文书
2016学校先进集体事迹材料
2016/02/29 职场文书