资讯专栏INFORMATION COLUMN

【详】JS实现拖拽元素互换位置

lentrue / 1627人阅读

摘要:只有在可放置的元素上面松开鼠标才会触发事件,所以这个是被放置的节点。

写在前面的废话

大家好,我是练习js时长接近两年半的个人练习生--李大雷

算了,直接 鸡,你太美~

应用场景

很多时候,我们需要让用户来自定义自己想要的菜单顺序,或者一些按钮的排序,那么这个时候,怎么给用户自定义顺序呢?
拖拽无疑是最简单易懂的,因为玩过手机的都知道怎么拖动桌面的app来改变位置。

那么要怎么做呢?最简单的方式肯定是用H5的拖放啦~

一些你需要了解的基础知识

首先我们先来看看,这两个单词,drag--拖,drop--放,从这里就很容易看出来,这里的操作逻辑了。
我们来看看有哪些事件可以给我们使用。

被我们拖的元素(按住鼠标)

ondragstart - 用户开始拖动元素时触发

ondrag - 元素正在拖动时触发

ondragend - 用户完成元素拖动后触发

释放拖拽元素时触发的事件(松开鼠标)

ondragenter - 当被鼠标拖动的对象进入其容器范围内时触发此事件

ondragover - 当某被拖动的对象在另一对象容器范围内拖动时触发此事件

ondragleave - 当被鼠标拖动的对象离开其容器范围内时触发此事件

ondrop - 在一个拖动过程中,释放鼠标键时触发此事件

我们来举例子说明一下

假设有div A和div B,当我按住A,开始拖动(A dragstart触发一次)(drag在你移动的时候不断触发),然后你经过了B(B触发了dragenter事件),然后你在B里疯狂摩擦(那就疯狂触发B的dragover,这句话怎么越读越不对劲?),然后你从B中出来(那就触发了B的dragleave),然后又进入B中(并且放开鼠标,那么就会触发B的drop和A的dragend);

对于A来说,它的事件就前面3个,对于B来说,它的事件就是后面4个;
A是攻,那么B就是受了。当然你也可以自攻自受,就像孟德尔的自交豌豆一样
我们下面做的拖拽也是自攻自受的情况,因为你可能拖动A和B交换,也可能拖动B来和A交换位置。

一些需要注意的点:

如果只需要拖动外层div,请务必把子元素的draggable属性设置为false(如果子元素里面有默认可拖动元素,则需要把里面的可拖动元素的属性设置为false);不然会引起很多奇怪的现象(比如你想拖一个包含图片的div,结果只把图片拖出来了);

链接和图片是默认可以拖动的;

ondragenter和ondragleave可能会触发多次,如果你把A拖动到B里,B一个大div设置了enter和leave事件,但是它里面还有很多子div,那么每进出一个子div,都会触发一次enter和leave事件。

开始操刀

这个标题的cao是第一声。
经过我们上面的一顿基础知识学习以后呢,我们就很容易想清楚这个实现逻辑。

把A设置为可以拖动,当A拖动到B的时候,我们就互换A和B两个dom节点。

至于怎么互换呢?我们可以直接调换两个节点的内容,或者我们调换两个dom节点的位置两种方法,这里我用的是第一种方法,第二种留给大家去尝试啦~

1. 我们先写一个大概的样式

2. html结构如下

${title}

3. 开始写逻辑,请仔细查看注释

//先定义两个变量来保存源元素,以及目标元素,还有记录一下上次交换的dom
//为什么要这一步呢?往后面看
let fromDom = null,
    toDom = null,
    lastDom = null;

//开始拖拽
function handleDragStart(e, dom) {
    //开始拖拽的时候,把来源保存下来
    fromDom = dom;
}
//拖拽中
function handleDrag(){
    console.log("如果你有业务逻辑的话,你可以写,但是我没有,抱歉")
}
//拖到了另一个div中,这个时候的dom就是另一个元素了哦
function handleDragEnter(e, dom) {
    //保存目标元素
    toDom = dom;
    if(fromDom == lastDom){
        //第一次调换
        //为什么要分为几次调换位置呢?
        //想一下,如果我刚A和B调换了位置,那么就是B和A了但是此时我的鼠标还没有松开!
        //那么我又移动到C,那么互换的位置就是B和C了,但是其实我一开始拖拽的是A,我只想换AC只是不小心路过了B!
        //因此我们这里就要使用一个lastDom来记录上次路过交换的DOM,同时也要区分第几次调换。
        swapDom(lastDom, toDom);
        //记录新的‘上一个dom’
        lastDom = toDom;
    }else{
        //这个防止enter多次触发
        if(lastDom == toDom){return;}
        //第N+1次调换,要先把上一个div的东西还原回去,再跟第三个div互换
        swapDom(fromDom,lastDom);
        swapDom(fromDom,toDom);
        //记录新的‘上一个dom’
        lastDom = toDom;
    }
}

//在B中移动
function handleDragOver(e, dom) {
    //默认无法把元素放置到其他元素当中,如果这个不写,无法交换div的innerHTML值,所以需要阻止默认事件,这一步很重要!!
    e.preventDefault();
}

//放手
function handleDragEnd(e,dom){
    //拖拽时松开鼠标就会会触发dragend事件,这个dom是拖拽的节点。
    //重置toDom,下次拖拽就是新拖拽了,fromDom和lastDom会在dragStart的时候重置
    toDom = null;
}
//有上面那个,其实这个可以省略了。
function handleDrop(e, dom) {
    //只有在可放置的元素上面松开鼠标才会触发drop事件,所以这个dom是被放置的dom节点。
    //重置toDom,下次拖拽就是新拖拽了,fromDom和lastDom会在dragStart的时候重置
    toDom = null;
}

//交换dom内容
function swapDom(from, to) {
    let temp = a.innerHTML;
    a.innerHTML = b.innerHTML;
    b.innerHTML = temp;
}
总结

其实我们用不上那么多事件回调,主要的是 开始拖拽保存来源,进入目标时,保存目标,并且经过判断后交换,交换完以后,我们就把目标重置,完事~
逻辑比较简单,不过写动态css比较麻烦(因为我们需要一些css的效果来分辨哪个是被你拖动的,那个又互换了位置之类的,有比较好的用户体验),刚开始写经常傻傻分不清是来源dom还是目标dom~

辣鸡源码

此部分适合新手玩家,因为自己只是随意写写,并没有写得很规范,希望大家不要学习!





    
    



    
一些你可能不感兴趣的后语

其实在没有这个drag之前,是用鼠标事件来实现的,这里就简单讲讲思路好了,懒得写了~

注册mousedown事件,

在mousedown触发的时候注册mousemove事件,根据鼠标移动的位置来定位点击的dom,也就是让这个元素跟着你的鼠标移动(你的dom得绝对定位哦),这里比较麻烦的就是一些边界的判定,因为你的鼠标能到边界,但是你的div不一定可以(div面积比较大),而且根据业务不同,你也可能有不同的操作,这里因人而异啦~

在mousedown里也注册mouseup事件,mouseup的作用就是把mousemove事件清空,因为要每一次鼠标按下去的时候才能有mousemove事件。

至于交换的话,上面也有说了。

谢谢大家,希望大家写代码不要像cxk。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/54857.html

相关文章

  • JS实现拖拽元素互换位置

    摘要:只有在可放置的元素上面松开鼠标才会触发事件,所以这个是被放置的节点。 写在前面的废话 大家好,我是练习js时长接近两年半的个人练习生--李大雷 算了,直接 鸡,你太美~ 应用场景 很多时候,我们需要让用户来自定义自己想要的菜单顺序,或者一些按钮的排序,那么这个时候,怎么给用户自定义顺序呢?拖拽无疑是最简单易懂的,因为玩过手机的都知道怎么拖动桌面的app来改变位置。 那么要怎么做呢?最简...

    AaronYuan 评论0 收藏0
  • HTML5拖放API Drag and Drop

    摘要:此文研究中的拖放接口,提供各个属性和方法的说明,解决拖放过程中的拖拽数据对象存储和获取问题。方法增加一个拖拽数据对象到属性中,并返回增加的拖拽数据对象。若拖拽数据对象是文本字符串类型,通过回调函数获取拖拽数据中的字符串数据。 此文研究Web API中的拖放接口,提供各个属性和方法的说明,解决拖放过程中的拖拽数据对象存储和获取问题。 拖放API作用到两个目标对象,分别是拖拽目标对象和放置...

    dantezhao 评论0 收藏0
  • 前端基础进阶(十):面向对象实战之封装拖拽对象

    摘要:前面几篇文章,我跟大家分享了的一些基础知识,这篇文章,将会进入第一个实战环节利用前面几章的所涉及到的知识,封装一个拖拽对象。不封装对象直接实现利用原生封装拖拽对象通过扩展来实现拖拽对象。 showImg(https://segmentfault.com/img/remote/1460000008699587); 前面几篇文章,我跟大家分享了JavaScript的一些基础知识,这篇文章,...

    Eidesen 评论0 收藏0
  • 从GGEditor的一个案例看JS原生拖拽功能

    摘要:涉及部分的,不会对理解全局产生干扰。在上监听事件,当为画布时,通过创建一个蓝色虚线框移动的时候,更新的位置在上监听事件,落在画布时,创建一个的节点从而完成整个拖拽添加元素的功能。 showImg(https://segmentfault.com/img/remote/1460000019564977);showImg(https://segmentfault.com/img/remot...

    PingCAP 评论0 收藏0

发表评论

0条评论

lentrue

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<