如何在拖拽元素时扩大默认滚动触发区域?
问题
我有一个可拖拽排序的元素列表,当前拖拽时只能通过将鼠标停在屏幕顶部和底部极小的30px区域触发滚动,想把触发滚动的区域扩大到120px。
相关代码
HTML代码
<div id='mainparent'> <!-- 主界面容器,滚动发生在这里 --> <div id='DivTesting'> <!-- 包含可重复元素的父容器 --> <div id='SavedIdeaDivContainer1'> <!-- 可拖拽排序的重复元素1 --> </div> <div id='SavedIdeaDivContainer2'> <!-- 这类元素有很多,数十个以上 --> </div> <div id='SavedIdeaDivContainer3'> </div> </div> <div id="itemClip" class="item itemClip hide"></div> </div>
原始JavaScript代码
var elements = document.querySelectorAll('.box .item'); var targetEl; var wrapper = document.querySelector(".box"); var itemClip = document.getElementById("itemClip"); var scopeObj; // === 事件绑定 === for (var j = 0, max = elements.length; j < max; j++) { elements[j].addEventListener("dragstart", handleDrag); elements[j].addEventListener("dragend", handleDragEnd); elements[j].addEventListener("dragenter", handleDragEnter); elements[j].addEventListener("touchstart", handleTouch); elements[j].addEventListener("touchend", handleTouchEnd); elements[j].addEventListener("touchmove", handleTouchMove); } // === 函数集合 === function handleDrag(event) { targetEl = event.target; targetEl.classList.add("onDrag"); } function handleDragEnd(event) { targetEl.classList.remove("onDrag"); } function handleDragEnter(event) { wrapper.insertBefore(targetEl, event.target.closest(".item")); } function handleTouch(event) { defineScope(elements); targetEl = event.target; itemClip.style.top = event.changedTouches[0].clientY + "px"; itemClip.style.left = event.changedTouches[0].clientX + "px"; itemClip.innerText = event.target.innerText; itemClip.classList.remove("hide"); targetEl.classList.add("onDrag"); } function handleTouchEnd(event) { itemClip.classList.add("hide"); targetEl.classList.remove("onDrag"); } function handleTouchMove(event) { itemClip.style.top = event.changedTouches[0].clientY + "px"; itemClip.style.left = event.changedTouches[0].clientX + "px"; hitTest(event.changedTouches[0].clientX, event.changedTouches[0].clientY); } function hitTest(thisX, thisY) { for (var j = 0, max = scopeObj.length; j < max; j++) { if (thisX > scopeObj[j].startX && thisX < scopeObj[j].endX) { if (thisY > scopeObj[j].startY && thisY < scopeObj[j].endY) { wrapper.insertBefore(targetEl, scopeObj[j].target); return; } } } } function defineScope(elementArray) { scopeObj = []; for (var j = 0, max = elementArray.length; j < max; j++) { var newObj = {}; newObj.target = elementArray[j]; newObj.startX = elementArray[j].offsetLeft; newObj.endX = elementArray[j].offsetLeft + elementArray[j].offsetWidth; newObj.startY = elementArray[j].offsetTop; newObj.endY = elementArray[j].offsetTop + elementArray[j].offsetHeight; scopeObj.push(newObj); } }
修改后的解决方案代码
以下是添加了120px滚动触发区域逻辑的完整JavaScript代码:
var elements = document.querySelectorAll('.box .item'); var targetEl; var wrapper = document.querySelector(".box"); var itemClip = document.getElementById("itemClip"); var scopeObj; // 配置:滚动触发区域高度(修改这里即可调整范围) var SCROLL_TRIGGER_ZONE = 120; // 配置:滚动速度 var SCROLL_SPEED = 8; // 滚动定时器,控制连续滚动 var scrollTimer; // === 事件绑定 === for (var j = 0, max = elements.length; j < max; j++) { elements[j].addEventListener("dragstart", handleDrag); elements[j].addEventListener("dragend", handleDragEnd); elements[j].addEventListener("dragenter", handleDragEnter); elements[j].addEventListener("touchstart", handleTouch); elements[j].addEventListener("touchend", handleTouchEnd); elements[j].addEventListener("touchmove", handleTouchMove); } // === 函数集合 === function handleDrag(event) { targetEl = event.target; targetEl.classList.add("onDrag"); // 拖拽开始时监听鼠标移动,用于检测滚动触发 document.addEventListener("mousemove", checkScrollOnDrag); } function handleDragEnd(event) { targetEl.classList.remove("onDrag"); // 拖拽结束时移除监听并清除滚动定时器 document.removeEventListener("mousemove", checkScrollOnDrag); clearInterval(scrollTimer); } function handleDragEnter(event) { wrapper.insertBefore(targetEl, event.target.closest(".item")); } function handleTouch(event) { defineScope(elements); targetEl = event.target; itemClip.style.top = event.changedTouches[0].clientY + "px"; itemClip.style.left = event.changedTouches[0].clientX + "px"; itemClip.innerText = event.target.innerText; itemClip.classList.remove("hide"); targetEl.classList.add("onDrag"); } function handleTouchEnd(event) { itemClip.classList.add("hide"); targetEl.classList.remove("onDrag"); // 触摸结束时清除滚动定时器 clearInterval(scrollTimer); } function handleTouchMove(event) { itemClip.style.top = event.changedTouches[0].clientY + "px"; itemClip.style.left = event.changedTouches[0].clientX + "px"; hitTest(event.changedTouches[0].clientX, event.changedTouches[0].clientY); // 触摸移动时检测滚动触发 checkScrollOnTouch(event.changedTouches[0].clientY); } function hitTest(thisX, thisY) { for (var j = 0, max = scopeObj.length; j < max; j++) { if (thisX > scopeObj[j].startX && thisX < scopeObj[j].endX) { if (thisY > scopeObj[j].startY && thisY < scopeObj[j].endY) { wrapper.insertBefore(targetEl, scopeObj[j].target); return; } } } } function defineScope(elementArray) { scopeObj = []; for (var j = 0, max = elementArray.length; j < max; j++) { var newObj = {}; newObj.target = elementArray[j]; newObj.startX = elementArray[j].offsetLeft; newObj.endX = elementArray[j].offsetLeft + elementArray[j].offsetWidth; newObj.startY = elementArray[j].offsetTop; newObj.endY = elementArray[j].offsetTop + elementArray[j].offsetHeight; scopeObj.push(newObj); } } // 鼠标拖拽时检测滚动触发 function checkScrollOnDrag(e) { checkScrollZone(e.clientY); } // 触摸拖拽时检测滚动触发 function checkScrollOnTouch(clientY) { checkScrollZone(clientY); } // 核心逻辑:检测位置是否在滚动区域,执行滚动并更新元素范围 function checkScrollZone(clientY) { var mainParent = document.getElementById('mainparent'); // 清除之前的定时器,避免重复触发 clearInterval(scrollTimer); // 顶部触发区域:距离屏幕顶部0到SCROLL_TRIGGER_ZONE像素 if (clientY <= SCROLL_TRIGGER_ZONE) { scrollTimer = setInterval(function() { mainParent.scrollTop -= SCROLL_SPEED; // 滚动后更新元素位置范围,确保排序逻辑正常 defineScope(elements); }, 20); } // 底部触发区域:距离屏幕底部0到SCROLL_TRIGGER_ZONE像素 else if (clientY >= window.innerHeight - SCROLL_TRIGGER_ZONE) { scrollTimer = setInterval(function() { mainParent.scrollTop += SCROLL_SPEED; // 滚动后更新元素位置范围,确保排序逻辑正常 defineScope(elements); }, 20); } }
关键改动说明
- 新增配置常量:
SCROLL_TRIGGER_ZONE = 120,直接定义触发滚动的区域高度,后续调整只需修改这个值。 - 添加滚动监听:鼠标拖拽时绑定
mousemove事件,触摸拖拽时在touchmove中触发检测,确保两种拖拽方式都支持滚动触发。 - 连续滚动控制:使用定时器实现平滑连续滚动,滚动时同步更新
scopeObj(元素位置范围),保证拖拽排序逻辑不受滚动影响。 - 边界判断逻辑:通过
window.innerHeight计算底部触发区域,确保在不同屏幕尺寸下都能正确识别底部120px范围。
内容的提问来源于stack exchange,提问作者king_anton
相关产品推荐
相关产品推荐

