前端页面元素拖动
最近工作不是很忙,所以借这个时间整理一下之前遇到的问题,记录一下相关的解决方案。
首先整理一下页面元素拖动实现的思路:
- 由于页面上可拖动的元素数量是不固定的,所以不应该把事件监听函数绑定到每个元素上,可以使用事件代理,将事件监听函数绑定到父级元素上。
- 移动位置的计算有2种思路,第一种是在mousedown事件触发时,记录鼠标事件的位置和元素的位置,然后mousemove事件每次触发,用当前的鼠标事件位置减mousedown事件的鼠标位置,移动的距离,再加上mousedown事件触发时记录的元素左边和上边的距离,即可计算出新的位置,简单地说就是鼠标每次移动后的位置与鼠标初始位置比较,得出移动的距离。第二种就是每次mousemove事件触发,都与上一次的位置进行比较;这种方法算起来比较麻烦,所以我采用第一种计算方法。
- 具体计算方法是这样的,在mousedown触发后,记录鼠标的位置,即e.clientX,e.clientY,以及元素距离左边和上边的距离,e.target.offsetLeft和e.target.offsetTop,因为我将父级元素的position设为relative,所以这两个值是元素边框外侧到父元素边框内侧的距离,在mousemove触发时,记录鼠标的新位置,即e.clientX,e.clientY,然后用 鼠标新位置 - 鼠标初始位置 + 元素初始左、上距离 算出新的位置,然后将元素position设为absolute,计算出的值即为left和top的值。
- 有2个需要注意的地方,第一,mousemove事件与mousedown,mouseup2个并不是连续的事件,当鼠标移动至少1个像素时,mousemove事件就会触发,与鼠标是否按下无关,所以需要在事件处理函数中校验拖动的元素是否为同一个对象;第二,还是因为mousemove事件与mousedown,mouseup2个并不是连续的事件,必须要保证mousemove事件是在mousedown事件后触发的,即鼠标按下,然后移动,不能是,鼠标按下,鼠标松开,然后移动。对于这两个问题,解决思路是一致的,都是在父级作用域中定义变量来控制,对于第一个问题,在mousedown事件触发后,将目标元素对象赋值给定义好的变量,然后在mousemove事件中校验事件对象元素是否为同一个元素,对于第二个问题,在父级作用域中定义一个flag,当mousedown触发,设为true,mouseup触发,设为false,在mousemove触发,校验是否为true就行了。
示例代码如下:
1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8" /> 5 <meta http-equiv="X-UA-Compatible" content="IE=edge" /> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 7 <title>Document</title> 8 <style> 9 .rect { 10 width: 100px; 11 height: 100px; 12 background-color: #eee; 13 margin: 5px; 14 } 15 .box { 16 position: relative; 17 } 18 </style> 19 </head> 20 <body> 21 <div class="box"> 22 <p class="rect"></p> 23 <p class="rect"></p> 24 <p class="rect"></p> 25 <p class="rect"></p> 26 <p class="rect"></p> 27 <p class="rect"></p> 28 <p class="rect"></p> 29 <p class="rect"></p> 30 <p class="rect"></p> 31 <p class="rect"></p> 32 <p class="rect"></p> 33 <p class="rect"></p> 34 <p class="rect"></p> 35 </div> 36 37 <script> 38 let position = { 39 x: 0, 40 y: 0, 41 left: 0, 42 top: 0, 43 } 44 let target = null 45 isDown = false 46 let box = document.querySelector(".box") 47 box.addEventListener("mousedown", function (e) { 48 if (!e.target.className.includes("rect")) return 49 target = e.target 50 position.x = e.clientX 51 position.y = e.clientY 52 position.left = e.target.offsetLeft 53 position.top = e.target.offsetTop 54 isDown = true 55 }) 56 57 box.addEventListener("mousemove", function (e) { 58 if (!e.target.className.includes("rect")) return 59 if (!isDown) return 60 target.style.position = "absolute" 61 let mx = e.clientX 62 let my = e.clientY 63 let myLeft = mx - position.x + position.left 64 let myTop = my - position.y + position.top 65 66 console.log({ myLeft, myTop }) 67 target.style.left = myLeft + "px" 68 target.style.top = myTop + "px" 69 }) 70 box.addEventListener("mouseup", function (e) { 71 if (isDown) isDown = false 72 }) 73 </script> 74 </body> 75 </html>