JavaScript进阶之实现拖拽(上)
最近做到的react项目就有一个拖拽的需求,然后大概搜索了一下,找到了star比较高的
react-dnd
库,但是阅读react-dnd
的官方文档还是有点难受的
,因为概念性比较强
,所以在介绍react-dnd
之前我们来实现原生拖拽
原生实现拖拽
Mouse事件实现拖拽
在h5之前,原生实现拖拽是根据
Mouse事件来实现的
,需要用到以下这三个事件mousedown
,mouseup
,mousemove
- mousedown 事件在指针设备按钮按下时触发。
- mouseup事件在指针设备按钮抬起时触发。
- 当指针设备( 通常指鼠标 )在元素上移动时, mousemove 事件被触发。
JavaScript三大家族


明白了上述👆三个事件方法的作用以及
JavaScript三大家族
,我们来实现个简单版的拖拽
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body> <img id="ball" src="https://js.cx/clipart/ball.svg" alt=""> <script> const ball=document.querySelector("#ball") ball.onmousedown = function(event) { let shiftX = event.clientX - ball.getBoundingClientRect().left; let shiftY = event.clientY - ball.getBoundingClientRect().top; ball.style.position = 'absolute'; ball.style.zIndex = 1000; document.body.append(ball); moveAt(event.pageX, event.pageY); // 移动现在位于坐标 (pageX, pageY) 上的球 // 将初始的偏移考虑在内 function moveAt(pageX, pageY) { ball.style.left = pageX - shiftX + 'px'; ball.style.top = pageY - shiftY + 'px'; } function onMouseMove(event) { moveAt(event.pageX, event.pageY); } // 在 mousemove 事件上移动球 document.addEventListener('mousemove', onMouseMove); // 放下球,并移除不需要的处理程序 ball.onmouseup = function() { document.removeEventListener('mousemove', onMouseMove); ball.onmouseup = null; }; }; ball.ondragstart = function() { return false; }; </script></body></html>
效果如下:

注意
ball.ondragstart = function() { return false;};
如果不设置这段代码,会发生奇怪的现象,这是因为浏览器有自己的对图片和一些其他元素的拖放处理
,会在我们拖放时自动运行,这与我们的拖放处理产生了冲突。
HTML 拖放(Drag and Drop)
拖拽事件
HTML 的 drag & drop 使用了
DOM event model
以及从mouse events
继承而
来的drag events
。一个典型的drag
操作是这样开始的:用户用鼠标选中一个可拖动的(draggable)元素,移动鼠标到一个可放置的(droppable)元素,然后释放鼠标。 在操作期间,会触发一些事件类型,有一些事件类型可能会被多次触发(比如drag 和 dragover 事件类型)

- drag: 拖拽源
- drop:拖拽源最终放置的目标
- DataTransfer 对象:退拽对象用来传递的媒介,使用一般为Event.dataTransfer。
- draggable 属性:就是标签元素要设置draggable=true
- ondragstart 事件:当拖拽元素开始被拖拽的时候触发的事件,此事件作用在被拖曳元素上
- ondragenter 事件:当拖曳元素进入目标元素的时候触发的事件,此事件作用在目标元素上
- ondragover 事件:拖拽元素在目标元素上移动的时候触发的事件,此事件作用在目标元素上
- ondrop 事件:被拖拽的元素在目标元素上同时鼠标放开触发的事件,此事件作用在目标元素上
- ondragend 事件:当拖拽完成后触发的事件,此事件作用在被拖曳元素上
- Event.preventDefault()方法:阻止默认的些事件方法等执行。在ondragover中一定要执行preventDefault(),否则ondrop事件不会被触发。另外,如果是从其他应用软件或是文件中拖东西进来,尤其是图片的时候,默认的动作是显示这个图片或是相关信息,并不是真的执行drop。此时需要用用document的ondragover事件把它直接干掉。Event.effectAllowed 属性:就是拖拽的效果。
注意理解上述👆的概念有注意理解
react-dnd
库的api
举个例子🌰:
<!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>Document</title> <style> body { font: 16px Arial, Helvetica, sans-serif; } li { width:200px; height: 40px; text-align: center; line-height: 40px; border:1px dashed #cccccc; cursor: pointer; user-select: none; background-color: white; list-style: none; } .more { border-top: 1px dotted rgb(196, 196, 196); font-size: 12px; padding-top: 10px; } .more, .more a { color: rgb(96, 96, 96); } </style> </head> <body> <ul> <li draggable="true" ondragend="dragEnd()" ondragover="dragOver(event)" ondragstart="dragStart(event)" > Apples </li> <li draggable="true" ondragend="dragEnd()" ondragover="dragOver(event)" ondragstart="dragStart(event)" > Oranges </li> <li draggable="true" ondragend="dragEnd()" ondragover="dragOver(event)" ondragstart="dragStart(event)" > Bananas </li> <li draggable="true" ondragend="dragEnd()" ondragover="dragOver(event)" ondragstart="dragStart(event)" > Strawberries </li> </ul> <script> var selected; const li = document.createElement("li"); function dragOver(e) { // 向前拖拽 向后拖拽 // 拖动目标(drop)是不是在拖拽源(drag)的前面 if (isBefore(selected, e.target)){ e.target.parentNode.insertBefore(selected, e.target); }else {e.target.parentNode.insertBefore(selected, e.target.nextSibling);} } function dragEnd() { selected = null; } function dragStart(e) { selected = e.target; console.log(selected) } function isBefore(el1, el2) { var cur; if (el2.parentNode === el1.parentNode) { for (cur = el1.previousSibling; cur; cur = cur.previousSibling) { if (cur === el2) return true; } } else return false; } </script> </body></html>
效果图:

是不是很接近我们的
需求啦
自定义拖动图像
拖动过程中,
浏览器会在鼠标旁显示一张默认图片
。当然,应用程序也可以通过setDragImage() 方法
自定义一张图片,如下面的例子所示。
function dragStart(e) { selected = e.target; dragstart_handler(e) // 延时是为了浏览器能生成拖拽图片 setTimeout(()=>{ selected.style.opacity='0' },0) } function dragstart_handler(ev) { var img = new Image(); img.src = 'https://js.cx/clipart/ball.svg'; ev.dataTransfer.setDragImage(img, 10, 10); }

定义拖动效果
dropEffect
属性用来控制拖放操作中用户给予的反馈。它会影响到拖动过程中浏览器显示的鼠标样式。比如,当用户悬停在目标元素上的时候,浏览器鼠标也许要反映拖放操作的类型。
有 3 个效果可以定义:
- copy 表明被拖动的数据将从它原本的位置拷贝到目标的位置。
- move 表明被拖动的数据将被移动。
- link 表明在拖动源位置和目标位置之间将会创建一些关系表格或是连接。
转载自:https://juejin.cn/post/6844904158273765384