基于Html5实现的react拖拽排序组件示例


Posted in HTML / CSS onAugust 13, 2018

拖拽排序组件Github地址:https://github.com/VicEcho/VDraggable.git

因为使用了react.js技术栈,所以封装优先考虑输入和输出。基于数据驱动去渲染页面、控制拖拽元素的顺序。

由于我不考虑兼容IE8等旧版本浏览器,拖拽的效果采用了HTML5的拖放(Drag 和 drop)。当然,如果要求兼容性丰富,使用鼠标点击的相关事件也很简单。

实现的效果如下:

基于Html5实现的react拖拽排序组件示例

第一步是先了解H5拖放的相关属性,MDN上有详细的说明,链接为https://developer.mozilla.org/en-US/docs/Web/Events/dragstart

有一点需要注意的是,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重新去渲染,以此控制顺序。

根据公司的要求,在此基础上,我还实现了拖拽复制的功能,这个等下次自己不懒的时候再记录下来。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

HTML / CSS 相关文章推荐
CSS3 渐变(Gradients)之CSS3 线性渐变
Jul 08 HTML / CSS
浅析两列自适应布局的3种思路
May 03 HTML / CSS
简单掌握CSS3将文字描边及填充文字颜色的方法
Mar 07 HTML / CSS
css3让div随鼠标移动而抖动起来
Feb 10 HTML / CSS
纯CSS3实现圆角效果(含IE兼容解决方法)
May 07 HTML / CSS
基于CSS3实现立方体自转效果
Mar 01 HTML / CSS
利用CSS3的线性渐变linear-gradient制作边框的示例
Jun 02 HTML / CSS
HTML5之SVG 2D入门11—用户交互性(动画)介绍及应用
Jan 30 HTML / CSS
HTML5如何为形状图上颜色怎么绘制具有颜色和透明度的矩形
Jun 23 HTML / CSS
HTML5 解决苹果手机不能自动播放音乐问题
Dec 27 HTML / CSS
VSCode 自定义html5模板的实现
Dec 05 HTML / CSS
VW、VH适配移动端的解决方案与常见问题
May 21 HTML / CSS
基于HTML5的WebSocket的实例代码
Aug 15 #HTML / CSS
HTML5中canvas中的beginPath()和closePath()的重要性
Aug 24 #HTML / CSS
Html5移动端弹幕动画实现示例代码
Aug 27 #HTML / CSS
Html5页面在微信端的分享的实现方法
Aug 30 #HTML / CSS
canvas粒子动画背景的实现示例
Sep 03 #HTML / CSS
html5中去掉input type date默认样式的方法
Sep 06 #HTML / CSS
html5新特性与用法大全
Sep 13 #HTML / CSS
You might like
PHP 文件类型判断代码
2009/03/13 PHP
PHP遍历目录函数opendir()、readdir()、closedir()、rewinddir()总结
2014/11/18 PHP
Yii rules常用规则示例
2016/03/15 PHP
Javascript 个人笔记(没有整理,很乱)
2007/07/07 Javascript
javascript向flash swf文件传递参数值注意细节
2012/12/11 Javascript
商城常用滚动的焦点图效果代码简单实用
2013/03/28 Javascript
jquery1.10给新增元素绑定事件的方法
2014/03/06 Javascript
Ajax局部更新导致JS事件重复触发问题的解决方法
2014/10/14 Javascript
js实现新浪微博首页效果
2015/10/16 Javascript
vue 请求后台数据的实例代码
2017/06/22 Javascript
JavaScript之Canvas_动力节点Java学院整理
2017/07/04 Javascript
Vue实现用户自定义字段显示数据的方法
2018/08/28 Javascript
解决layui的使用以及针对select、radio等表单组件不显示的问题
2019/09/05 Javascript
微信小程序间使用navigator跳转传值问题实例分析
2020/03/27 Javascript
[51:20]完美世界DOTA2联赛PWL S2 Magma vs PXG 第一场 11.28
2020/12/01 DOTA
深入理解Python变量与常量
2016/06/02 Python
python验证码识别的示例代码
2017/09/21 Python
python如何爬取个性签名
2018/06/19 Python
Pycharm使用之设置代码字体大小和颜色主题的教程
2019/07/12 Python
python滑块验证码的破解实现
2019/11/10 Python
关于Flask项目无法使用公网IP访问的解决方式
2019/11/19 Python
Python标准库itertools的使用方法
2020/01/17 Python
django 外键创建注意事项说明
2020/05/20 Python
Python接口测试环境搭建过程详解
2020/06/29 Python
如何在VSCode下使用Jupyter的教程详解
2020/07/13 Python
CSS3 中的@keyframes介绍
2014/09/02 HTML / CSS
俄罗斯品牌服装和鞋子在线商店:BRIONITY
2020/03/26 全球购物
ruby如何进行集成操作?Ruby能进行多重继承吗?
2013/10/16 面试题
红领巾广播站广播稿
2014/02/01 职场文书
小学生新年寄语
2014/04/03 职场文书
数控机床专业自荐信
2014/05/19 职场文书
机械操作工岗位职责
2014/08/08 职场文书
小学体育组工作总结2015
2015/07/21 职场文书
导游词之四川熊猫基地
2020/01/13 职场文书
JS中如何优雅的使用async await详解
2021/10/05 Javascript
win10以太网连接不上怎么办?Win10连接以太网详细教程
2022/04/08 数码科技