You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

小屏及移动设备下原生JS拖拽功能运行异常问题

问题根因

两个异常都是触摸事件基础用法错误导致的:

  • 元素消失:触摸事件对象本身没有clientX/clientY属性,这两个是鼠标事件的标准属性。触摸事件的触点坐标存放在事件对象的touches/changedTouches触点列表数组中,直接读取s.clientX会得到undefined,计算出的位移差为NaN,给元素设置无效的top/left值后,元素位置会直接异常丢失。
  • 触摸穿透:代码存在两个问题导致穿透:一是初始加载时就给ele绑定了touchmove/touchend回调,touchstart触发后又给#lock_image重复绑定了相同回调,事件会重复触发;二是没有调用preventDefault()阻止触摸事件的默认行为,浏览器会默认执行页面滚动、事件冒泡穿透逻辑,触摸动作会直接作用到下层的body元素。
    另外注意:touchmove事件在移动端浏览器默认是passive模式,该模式下调用preventDefault()会失效,绑定事件时需要显式关闭passive配置。
修复方案
  1. 所有触摸事件统一从touches[0](第一个触点,适配单指拖拽场景)读取坐标值
  2. 移除重复的事件绑定,仅在touchstart触发时绑定移动、结束事件,拖拽结束后立刻移除
  3. 在touchmove回调中阻止默认行为,绑定事件时配置passive: false保证阻止生效
  4. 确认被拖拽元素设置了非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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 04:06:20