HTML/CSS 拖拽Div在表格内相对鼠标位置偏移异常问题
问题根源
你遇到的偏移问题核心是绝对定位的参照基准不统一:
- 你在CSS中给
td设置了position: relative,表格内的卡片设置position: absolute时,会以最近的非static定位父元素(也就是td)为定位基准 - 你JS中计算卡片位置用的是
e.pageX/e.pageY,这两个值是相对于整个文档左上角的坐标 - 表格外的卡片父元素都是默认
position: static,绝对定位时默认参照文档根节点,所以坐标计算刚好匹配,没有偏移
修复方案
这里提供两种可行的修复方向,你可以根据需求选择:
方案1:统一拖拽元素的定位基准
拖拽开始时将正在拖拽的卡片临时挂载到body下,保证所有拖拽元素的绝对定位都以文档为基准,拖拽结束后再插回原位置对应节点。
需要修改mouseMoveHandler和mouseUpHandler两个函数:
const mouseMoveHandler = function (e) { const draggingRect = draggingEle.getBoundingClientRect(); if (!isDraggingStarted) { isDraggingStarted = true; placeholder = document.createElement('div'); placeholder.classList.add('placeholder'); draggingEle.parentNode.insertBefore( placeholder, draggingEle.nextSibling ); placeholder.style.height = `${draggingRect.height}px`; // 新增:将拖拽元素临时挂载到body document.body.appendChild(draggingEle); draggingEle.style.zIndex = '999'; // 避免被其他元素遮挡 } draggingEle.style.position = 'absolute'; draggingEle.style.top = `${e.pageY - mouse.y}px`; draggingEle.style.left = `${e.pageX - mouse.x}px`; // 后续交换逻辑调整为直接操作占位符 const prevEle = placeholder.previousElementSibling; const nextEle = placeholder.nextElementSibling; if (prevEle && isAbove(draggingEle, prevEle)) { swap(placeholder, prevEle); return; } if (nextEle && isAbove(nextEle, draggingEle)) { swap(nextEle, placeholder); } }; const mouseUpHandler = function () { // 新增:将拖拽元素插回占位符的位置 placeholder.parentNode.insertBefore(draggingEle, placeholder); placeholder && placeholder.parentNode.removeChild(placeholder); isDraggingStarted = false; // 移除自定义样式 draggingEle.style.removeProperty('top'); draggingEle.style.removeProperty('left'); draggingEle.style.removeProperty('position'); draggingEle.style.removeProperty('z-index'); mouse.x = null; mouse.y = null; draggingEle = null; document.removeEventListener('mousemove', mouseMoveHandler); document.removeEventListener('mouseup', mouseUpHandler); };
方案2:调整坐标计算逻辑
不想修改DOM结构的话,可以在计算卡片定位时,减去td元素的左上角偏移量,把文档坐标转换为相对于td的坐标:
const mouseMoveHandler = function (e) { const draggingRect = draggingEle.getBoundingClientRect(); if (!isDraggingStarted) { isDraggingStarted = true; placeholder = document.createElement('div'); placeholder.classList.add('placeholder'); draggingEle.parentNode.insertBefore( placeholder, draggingEle.nextSibling ); placeholder.style.height = `${draggingRect.height}px`; } // 新增:获取最近的td元素的偏移量,兼容页面滚动 const parentTd = draggingEle.closest('td'); const tdRect = parentTd ? parentTd.getBoundingClientRect() : {top:0, left:0}; const scrollTop = window.pageYOffset || document.documentElement.scrollTop; const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft; const tdOffsetTop = tdRect.top + scrollTop; const tdOffsetLeft = tdRect.left + scrollLeft; draggingEle.style.position = 'absolute'; // 计算坐标时减去td的偏移 draggingEle.style.top = `${e.pageY - mouse.y - tdOffsetTop}px`; draggingEle.style.left = `${e.pageX - mouse.x - tdOffsetLeft}px`; // 后续交换逻辑保持原有逻辑不变 const prevEle = draggingEle.previousElementSibling; const nextEle = placeholder.nextElementSibling; if (prevEle && isAbove(draggingEle, prevEle)) { swap(placeholder, draggingEle); swap(placeholder, prevEle); return; } if (nextEle && isAbove(nextEle, draggingEle)) { swap(nextEle, placeholder); swap(nextEle, draggingEle); } };
两种方案都可以解决表格内拖拽偏移的问题,第一种方案适用性更强,后续扩展跨单元格拖拽时也不需要额外调整坐标逻辑。
内容的提问来源于stack exchange,提问作者John M.T.
相关产品推荐
相关产品推荐

