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 字符串与数组转换函数[不用split与join]
Dec 13 Javascript
js遍历、动态的添加数据的小例子
Jun 22 Javascript
javascript实现图片延迟加载方法汇总(三种方法)
Aug 27 Javascript
基于BootStrap Metronic开发框架经验小结【六】对话框及提示框的处理和优化
May 12 Javascript
Node.js中process模块常用的属性和方法
Dec 13 Javascript
JS解决移动web开发手机输入框弹出的问题
Mar 31 Javascript
JavaScript实现自动跳转文本功能
May 25 Javascript
浅谈Vue.js应用的四种AJAX请求数据模式
Aug 30 Javascript
JavaScript多态与封装实例分析
Jul 27 Javascript
webpack4+Vue搭建自己的Vue-cli项目过程分享
Aug 29 Javascript
在JS循环中使用async/await的方法
Oct 12 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 注释规范
2012/03/29 PHP
PHP批量删除、清除UTF-8文件BOM头的代码实例
2014/04/14 PHP
CodeIgniter框架过滤HTML危险代码
2014/06/12 PHP
php获得网站访问统计信息类Compete API用法实例
2015/04/02 PHP
PHP获取二维数组中某一列的值集合
2015/12/25 PHP
PHP实现获取并生成数据库字典的方法
2016/05/04 PHP
PHP接收App端发送文件流的方法
2016/09/23 PHP
Mootools 1.2教程 函数
2009/09/15 Javascript
javascript对数组的常用操作代码 数组方法总汇
2011/01/27 Javascript
如何用JavaScript动态呼叫函数(两种方式)
2013/05/03 Javascript
JS 新增Cookie 取cookie值 删除cookie 举例详解
2014/10/10 Javascript
js验证身份证号有效性并提示对应信息
2015/10/19 Javascript
利用javascript实现的三种图片放大镜效果实例(附源码)
2017/01/23 Javascript
Nodejs--post的公式详解
2017/04/29 NodeJs
Node.js微信 access_token ( jsapi_ticket ) 存取与刷新的示例
2017/09/30 Javascript
让网站自动生成章节目录索引的多个js代码
2018/01/07 Javascript
Vuejs在v-for中,利用index来对第一项添加class的方法
2018/03/03 Javascript
layui表格checkbox选择全选样式及功能的实例
2018/03/07 Javascript
Angular中的ng-template及angular 使用ngTemplateOutlet 指令的方法
2018/08/08 Javascript
微信小程序开发实现的IP地址查询功能示例
2019/03/28 Javascript
灵活使用console让js调试更简单的方法步骤
2019/04/23 Javascript
js使用cookie实现记住用户名功能示例
2019/06/13 Javascript
Vue.js的模板语法详解
2020/02/16 Javascript
Python之父谈Python的未来形式
2016/07/01 Python
面向新手解析python Beautiful Soup基本用法
2020/07/11 Python
Pycharm学生免费专业版安装教程的方法步骤
2020/09/24 Python
Python爬虫之Selenium中frame/iframe表单嵌套页面
2020/12/04 Python
CSS3效果:自定义“W”形运行轨迹实例
2017/03/29 HTML / CSS
sort命令的作用和用法
2013/08/25 面试题
旅游专业职业生涯规划范文
2014/01/13 职场文书
2014年村官工作总结
2014/11/24 职场文书
委托书英文
2015/01/28 职场文书
股东大会通知
2015/04/24 职场文书
民事答辩状格式范文
2015/05/21 职场文书
三八红旗手先进事迹材料(2016推荐版)
2016/02/25 职场文书
pytorch MSELoss计算平均的实现方法
2021/05/12 Python