为何拖动DOM元素时它会跳转到窗口右侧?求解决方案
问题描述
尝试在mousedown事件中拖动元素一小段距离以显示下方新元素,但拖动时元素直接跳到窗口右侧。已尝试将position从relative改为absolute,也试过设置固定坐标和基于鼠标事件的相对坐标,结果都一样。
背景:平时常用Vue.js避免直接操作DOM(经验不足),这个问题最初出现在Vue项目中,已证实和Vue无关。
复现代码
HTML
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <link rel="icon" href="/favicon.ico"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Drag Drop Test</title> </head> <body> <div id="app" align="center" class="box"> <button id="red" class="button" >Red</button> <p id="from"></p> <p id="to"></p> <p id="result"></p> </div> </body> </html>
CSS
.box { position:relative; margin: auto; border: 3px solid #73AD21; width: 200px; height: 250px; } .button { position:relative; background-color: red; color: white; padding: 10px 20px; text-align: center; width: 100px; font-size: 16px; cursor: pointer; }
JavaScript
document.getElementById("red").addEventListener("mousedown", mouseDown); function mouseDown(e) { const el = e.target; // const x = e.pageX; // const y = e.pageY; const rect = el.getBoundingClientRect(); const x = rect.left + 20; const y = rect.top + 20; el.style.left = `${x}px` el.style.top = `${y}px` const newrect = el.getBoundingClientRect(); document.getElementById("from").innerHTML = "drag from: " + rect.left + ", " + rect.top; document.getElementById("to").innerHTML = "try to drag to: " + x + ", " + y; document.getElementById("result").innerHTML = "dragged to: " + newrect.left + ", " + newrect.top; }
问题原因与解决方法
核心问题
当元素设置为position: relative时,left和top属性的取值是相对于元素自身初始位置的偏移量,而getBoundingClientRect()返回的left/top是元素相对于浏览器视口的绝对坐标。直接把绝对坐标赋值给relative定位元素的left/top,相当于让元素从初始位置向右偏移rect.left+20像素,这自然会让元素跳到窗口右侧。
解决思路1:保持relative定位,计算相对偏移
如果要保留position: relative,需要计算相对于元素自身初始位置的偏移量,而非视口绝对坐标:
function mouseDown(e) { const el = e.target; // 获取元素当前的left/top值(初始为0,未设置时默认) const currentLeft = parseInt(window.getComputedStyle(el).left) || 0; const currentTop = parseInt(window.getComputedStyle(el).top) || 0; // 每次拖动增加20px偏移 const newLeft = currentLeft + 20; const newTop = currentTop + 20; el.style.left = `${newLeft}px`; el.style.top = `${newTop}px`; const newRect = el.getBoundingClientRect(); document.getElementById("from").innerHTML = "drag from: " + (newRect.left - newLeft) + ", " + (newRect.top - newTop); document.getElementById("to").innerHTML = "try to drag to: " + newLeft + ", " + newTop; document.getElementById("result").innerHTML = "dragged to: " + newRect.left + ", " + newRect.top; }
解决思路2:切换为absolute定位,基于父容器偏移
改用position: absolute时,需确保父容器(.box)是position: relative(你已经设置),此时元素的left/top是相对于父容器的:
- 修改CSS中.button的定位:
.button { position:absolute; /* 其他样式保持不变 */ }
- 修改JS计算相对于父容器的坐标:
function mouseDown(e) { const el = e.target; const parentRect = el.parentElement.getBoundingClientRect(); const elRect = el.getBoundingClientRect(); // 计算元素相对于父容器的当前位置 const relativeLeft = elRect.left - parentRect.left; const relativeTop = elRect.top - parentRect.top; // 偏移20px const newLeft = relativeLeft + 20; const newTop = relativeTop + 20; el.style.left = `${newLeft}px`; el.style.top = `${newTop}px`; const newRect = el.getBoundingClientRect(); document.getElementById("from").innerHTML = "drag from: " + elRect.left + ", " + elRect.top; document.getElementById("to").innerHTML = "try to drag to: " + (elRect.left + 20) + ", " + (elRect.top + 20); document.getElementById("result").innerHTML = "dragged to: " + newRect.left + ", " + newRect.top; }
内容的提问来源于stack exchange,提问作者quilkin
相关产品推荐
相关产品推荐

