touchmove与mousemove行为差异:移动端目标元素不变问题咨询
移动端touchmove事件target不更新的问题:原因与解决方案
这个问题我之前也碰到过,本质是鼠标事件和触摸事件的目标绑定逻辑完全不同,咱们一步步拆解清楚:
一、为什么会出现这个差异?
- 鼠标事件(如
mousemove):每次鼠标移动时,浏览器都会实时检测光标当前所在的DOM元素,所以event.target会动态更新为光标下方的元素——这就是你在div1按下鼠标滑到div2时,mousemove的target会变成div2的原因。 - 触摸事件(如
touchmove):触摸的整个事件序列(touchstart→touchmove→touchend)是绑定在初始触摸的元素上的。也就是说,你在div1触发touchstart后,不管手指滑到哪个元素,后续touchmove的event.target都会一直是div1。这是浏览器为了保证触摸交互连贯性(比如拖拽元素)而设计的默认行为。
二、解决办法:手动获取触摸位置的元素
既然浏览器不会自动更新touchmove的target,我们可以在touchmove事件里,通过document.elementFromPoint()方法,根据当前触摸的坐标获取真正位于手指下方的元素,代替原来的event.target。
修改后的完整代码
<!DOCTYPE html> <html> <head> <title>Page Title</title> <style> div { display: inline-block; width: 150px; height: 150px; color: red; border: 1px solid black; } </style> </head> <body> <div id='div1'>div1</div> <div id='div2'>div2</div> <div id='div3'>div3</div> <script> let testString = ''; const handleMoveListener = (e) => { e.preventDefault(); e.stopPropagation(); let targetElement; // 区分鼠标事件和触摸事件 if (e.type === 'touchmove') { // 获取第一个触摸点的视口坐标 const touch = e.touches[0]; // 根据坐标定位当前元素 targetElement = document.elementFromPoint(touch.clientX, touch.clientY); } else { // 鼠标事件直接使用原生target targetElement = e.target; } // 确保元素有id时再拼接,避免报错 if (targetElement?.id) { testString += ' ' + targetElement.id; } } const handleEndHandler = (e) => { alert(testString); testString = ''; } let elementsArray = document.querySelectorAll("div"); elementsArray.forEach(function (elem) { elem.addEventListener('mousemove', handleMoveListener); elem.addEventListener('touchmove', handleMoveListener); elem.addEventListener('mouseup', handleEndHandler); elem.addEventListener('touchend', handleEndHandler); }); </script> </body> </html>
关键修改说明
- 统一了鼠标和触摸的移动事件处理函数,在内部通过
e.type区分事件类型 - 针对
touchmove事件:通过e.touches[0]获取触摸点的视口坐标,再用document.elementFromPoint(x, y)拿到当前手指下方的元素 - 加入了可选链判断
targetElement?.id,避免获取到null或非DOM元素时出现报错
额外注意点
- 如果页面存在滚动,需要加上
document.documentElement.scrollTop和document.documentElement.scrollLeft来修正坐标,确保elementFromPoint定位准确 - 保留
e.preventDefault()可以避免触发浏览器默认触摸行为(如页面滚动),防止干扰事件触发
内容的提问来源于stack exchange,提问作者Mahesh




