写在前面的废话

大家好,我是练习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结构如下

οndrag="handleDrag(event,this)"

οndragstart="handleDragStart(event,this)"

οndragοver="handleDragOver(event,this)"

οndragend="handleDragEnd(event,this)"

οndrοp="handleDrop(event,this)"

οndragenter="handleDragEnter(event,this)">

${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~

辣鸡源码

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

body {

margin: 0;

}

.box {

display: flex;

justify-content: flex-start;

flex-wrap: wrap;

}

.card {

flex: 1;

min-width: 26%;

max-width: calc(33.3% - 40px);

height: 200px;

margin: 30px 10px;

position: relative;

padding: 10px;

box-shadow: 0 2px 5px 0 #999;

border-radius: 5px;

border: 2px dashed transparent;

}

.card-name {

position: absolute;

top: 10px;

left: 10px;

line-height: 20px;

height: 20px;

}

.card-img {

position: relative;

padding-top: 20px;

box-sizing: border-box;

width: 100%;

height: 100%;

overflow: hidden;

}

.card-img img {

width: 100%;

height: 100%;

}

.dragging-over * {

pointer-events: none;

}

const htmlArr = [

{ title: '示例1-风景', src: 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2756575517,833879878&fm=200&gp=0.jpg' },

{ title: '示例2-风景', src: 'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=328517395,2303970886&fm=26&gp=0.jpg' },

{ title: '示例3-风景', src: 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1554369684535&di=1c1dbfbd4545ad0a05e12cbbbfe3eeef&imgtype=0&src=http%3A%2F%2Fpic41.nipic.com%2F20140601%2F18681759_143805185000_2.jpg' },

{ title: '示例4-风景', src: 'https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1554369684534&di=d6e34af6fce6564f9df6c4eecc27d2ce&imgtype=0&src=http%3A%2F%2Fimgsrc.baidu.com%2Fimgad%2Fpic%2Fitem%2F4d086e061d950a7b9138ff1000d162d9f3d3c9d1.jpg' },

]

let fromDom = null,

toDom = null,

lastDom = null;

function handleDragStart(e, dom) {

lastDom = fromDom = dom;

dom.style.border = "2px dashed #999";

dom.style.opacity = 0.4;

}

function handleDrop(e, dom) {

//只有在可放置的元素上面松开鼠标才会触发drop事件

console.log('drop');

dom.style.opacity = "";

fromDom = null;

toDom = null;

}

function handleDragEnd(e,dom){

//拖拽时松开鼠标就会会触发dragend事件

console.log('end');

dom.style.border = "2px dashed transparent";

dom.style.opacity = "";

toDom = null;

}

function handleDragEnter(e, dom) {

toDom = dom;

if(fromDom == lastDom){

//第一次调换

swapDom(lastDom, toDom);

lastDom = toDom;

}else{

//第N+1次调换,要先把上一个div的东西还原回去,再跟第三个div互换

//这个防止enter多次触发

if(lastDom == toDom){return;}

swapDom(fromDom,lastDom);

swapDom(fromDom,toDom);

lastDom = toDom;

}

}

function handleDragOver(e, dom) {

//默认无法把元素放置到其他元素当中,所以需要prevent

e.preventDefault();

e.dataTransfer.effectAllowed = "move";

}

function swapDom(a, b) {

//a和b的innerHTML互换

let temp = a.innerHTML;

a.innerHTML = b.innerHTML;

b.innerHTML = temp;

}

//生成dom结构

function createDom(arr) {

let body = document.getElementsByClassName('box')[0];

let html = [];

for (let i = 0, len = arr.length; i < len; i++) {

html.push(template(arr[i].title, arr[i].src));

}

body.innerHTML = html.join('');

}

//html模板,根据该模板动态生成dom节点

function template(title, src) {

let tpl = `

${title}

`

return tpl;

}

window.onload = function() {

createDom(htmlArr);

}

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

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

注册mousedown事件,

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

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

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

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

html元素拖动互换位置原理,【详】JS实现拖拽元素互换位置相关推荐

  1. JQuery 拖拽元素,并移动其他元素位置

    JQuery 拖拽元素,并移动其他元素位置 <!DOCTYPE html> <html> <head><meta charset="UTF-8&qu ...

  2. js 拖拽元素 鼠标速度过快问题

    自己写一个小工具,使用js拖拽元素时,鼠标速度过快时 元素跟不上鼠标 看网上有人说把mousemove事件绑定到dom上 试了不管用 偶然发现拖拽文字丝毫无卡顿  不知道是不是元素比较复杂的缘故? s ...

  3. vue 拖拽元素到任意位置

    vue 拖拽元素到任意位置 使用vue-drag-it-dude组件 npm install vue-drag-it-dude --save 参考地址:https://github.com/xzqyu ...

  4. js 拖拽元素 鼠标速度过快元素跟不上

    自己写一个拖拽元素,使用js拖拽元素时,鼠标速度过快时 元素跟不上鼠标 参考了网上的文章 ,里面对比了绑定到 body和document上的不同点js 拖拽元素 鼠标速度过快问题 明白过来是因为速度过 ...

  5. 原生JS的拖拽属性draggable(详解)

    摘要 作为h5新增的属性draggable,它能够给与一切的html元素拖动的效果.而在这个属性之下,也有着关于拖动效果的各个方法. 而这一篇文章,主要就是说一下关于draggable属性的使用以及工 ...

  6. 原生JS实现拖拽垂直进度条

    先看效果图 代码如下 <!DOCTYPE html> <html><head><meta http-equiv="Content-Type" ...

  7. html5 原生拖拽,原生JS实现拖拽效果

    这篇文章主要为大家详细介绍了原生JS实现拖拽效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 本文实例为大家分享了JS实现拖拽效果的具体代码,供大家参考,具体内容如下 ...

  8. 【记】Vue - 拖拽元素组件实现

    需求描述: 1.可实现PC/移动端元素拖拽移动 2.支持2种模式:1.元素跟随光标点放置2.元素在光标点平齐位置靠侧边吸附 市面上估计有很多这种组件和功能了,但我没找到合适的,用了VueUse的use ...

  9. js实现拖拽+碰撞+重力

    <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="C ...

最新文章

  1. servlet session listener
  2. centos7 apache2.4 多站点配置
  3. 转:WebApi(一)
  4. 一部分 数据 迁移_软件测试员12小时惊魂记:数据库迁移出大事故,如何测试?...
  5. SQL Server 数据库巡检脚本
  6. 计划备份mysql数据库
  7. Unity OnTriggerEnter不调用
  8. 继承演练 c# 1613706361
  9. HDU2017新生赛 找方块
  10. ExtJs + Struts2 + JSON 程序总结
  11. 俄罗斯方块游戏设计的有关问题
  12. USB转TTL接线方法
  13. pdf文件如何在线转换为jpg图片
  14. 平面设计专业学什么?大学平面设计主修课程有哪些?
  15. 维基百科的语料库下载以及信息提取笔记
  16. 腾讯会议使用OBS虚拟摄像头
  17. svn如何修改443端口
  18. 最强文件搜索神器——Everything
  19. c++ double 截取_c++ double 截取_C/C++ double取余函数
  20. 代码主题darcula_如何在带有Darcula主题的黑暗模式下使用NetBeans

热门文章

  1. javaIdea快捷键
  2. 机器视觉: LBP-TOP
  3. vscode本地端访问服务器工程
  4. 做网工10年,没人在30岁前和我讲这些(一)
  5. 【LeetCode】【esay】【69】x的平方
  6. 地平线 旭日X3 SDB开发板 (三) 工具链仿真/板上测试
  7. 网络监控软件造成的网站打不开问题
  8. 遍历json 对象的属性并且动态添加属性
  9. 2018北京建博会-智能家居展将于3月9日在北京老国展盛大开幕
  10. ***VIP工具包(共4套)