小屏及移动设备下原生JS拖拽功能运行异常问题
问题根因
两个异常都是触摸事件基础用法错误导致的:
- 元素消失:触摸事件对象本身没有
clientX/clientY属性,这两个是鼠标事件的标准属性。触摸事件的触点坐标存放在事件对象的touches/changedTouches触点列表数组中,直接读取s.clientX会得到undefined,计算出的位移差为NaN,给元素设置无效的top/left值后,元素位置会直接异常丢失。 - 触摸穿透:代码存在两个问题导致穿透:一是初始加载时就给
ele绑定了touchmove/touchend回调,touchstart触发后又给#lock_image重复绑定了相同回调,事件会重复触发;二是没有调用preventDefault()阻止触摸事件的默认行为,浏览器会默认执行页面滚动、事件冒泡穿透逻辑,触摸动作会直接作用到下层的body元素。
另外注意:touchmove事件在移动端浏览器默认是passive模式,该模式下调用preventDefault()会失效,绑定事件时需要显式关闭passive配置。
修复方案
- 所有触摸事件统一从
touches[0](第一个触点,适配单指拖拽场景)读取坐标值 - 移除重复的事件绑定,仅在
touchstart触发时绑定移动、结束事件,拖拽结束后立刻移除 - 在
touchmove回调中阻止默认行为,绑定事件时配置passive: false保证阻止生效 - 确认被拖拽元素设置了非static的定位属性(
position: absolute/fixed/relative),否则top/left样式不会生效
修复后的完整代码:
let x = 0; let y = 0; const ele = document.getElementById("textOverImage"); const lockImage = document.querySelector("#lock_image"); // 拖拽逻辑封装,适配窗口尺寸变化场景 function bindDragEvent() { if (window.innerWidth > 480) return; // 仅绑定touchstart到拖拽元素,避免重复绑定 ele.addEventListener('touchstart', handleStart, { passive: false }); } function handleStart(s) { // 取第一个触点的坐标 const touch = s.touches[0]; x = touch.clientX; y = touch.clientY; // 绑定移动、结束事件到容器,配置passive:false lockImage.addEventListener('touchmove', handleMove, { passive: false }); lockImage.addEventListener('touchend', handleEnd, { passive: false }); } function handleMove(s) { const touch = s.touches[0]; const dx = touch.clientX - x; const dy = touch.clientY - y; ele.style.top = `${ele.offsetTop + dy}px`; ele.style.left = `${ele.offsetLeft + dx}px`; ele.classList.remove('centered', 'customClass'); ele.classList.add('customMovement'); x = touch.clientX; y = touch.clientY; // 阻止默认滚动、穿透行为 s.preventDefault(); } function handleEnd() { // 拖拽结束移除事件,避免重复触发 lockImage.removeEventListener('touchmove', handleMove); lockImage.removeEventListener('touchend', handleEnd); } // 页面加载时绑定 bindDragEvent(); // 可选:监听窗口尺寸变化,横竖屏切换时重新适配 window.addEventListener('resize', bindDragEvent);
额外校验项
如果修复后仍有位置异常,检查customMovement类的CSS配置,确认存在position: absolute或其他非static定位属性,否则内联的top/left样式不会生效。
内容的提问来源于stack exchange,提问作者Mohammed_Helal
相关产品推荐
相关产品推荐

