实现网页拖拽滚动时,如何阻止链接默认跳转?
拖拽滚动场景下阻止链接默认跳转的解决方案
问题背景
基于拖拽滚动脚本实现了网页拖拽滚动功能,同时通过CSS移除了<a>标签和图片的默认可拖拽性,确保鼠标在这些元素上时也能拖拽页面。但拖拽页面后,点击链接时的e.preventDefault()无法阻止链接跳转。
当前使用代码
JavaScript代码
document.addEventListener('DOMContentLoaded', () => { let dragging; const pos = {}; const onClick = (e) => { if(!dragging) return; e.preventDefault(); // 无效:原因何在? e.stopImmediatePropagation(); }; const onMouseMove = (e) => { dragging = true; window.scroll(0, pos.top - (e.clientY - pos.y)); }; const onMouseUp = (e) => { dragging = false; e.target.removeEventListener('mousemove', onMouseMove); e.target.removeEventListener('mouseup', onMouseUp); e.target.removeEventListener('click', onClick); }; const onMouseDown = (e) => { pos.top = window.scrollY; pos.y = e.clientY; e.target.addEventListener('mousemove', onMouseMove); e.target.addEventListener('mouseup', onMouseUp); e.target.addEventListener('click', onClick); }; document.addEventListener('mousedown', onMouseDown); });
CSS代码
a, img { user-drag: none; -webkit-user-drag: none; }
HTML代码
<div style="height:200vh; background:lightblue; padding:1em;"> <h1>Drag to scroll me</h1> <a href="//stackoverflow.com" style="background:white; padding:1em">Dragging this link should NOT open the website (but does)</a> </div>
问题原因
事件触发顺序为mousedown → mousemove → mouseup → click。当前代码在mouseup中立刻把dragging设为false,等click事件触发时,dragging已经是false,导致onClick里的判断不生效,e.preventDefault()根本没执行。
解决方法
方法一:延迟重置拖拽状态
修改onMouseUp函数,用setTimeout延迟重置dragging,确保click事件先完成判断:
const onMouseUp = (e) => { // 延迟100ms重置dragging,让click事件先执行判断逻辑 setTimeout(() => { dragging = false; }, 100); e.target.removeEventListener('mousemove', onMouseMove); e.target.removeEventListener('mouseup', onMouseUp); e.target.removeEventListener('click', onClick); };
方法二:通过位移区分点击和拖拽(更可靠)
记录鼠标按下时的初始位置,在click事件中判断位移大小,超过阈值则判定为拖拽,阻止链接跳转:
document.addEventListener('DOMContentLoaded', () => { let isDragging = false; const pos = { top: 0, y: 0, startY: 0 }; const onClick = (e) => { // 位移超过5px判定为拖拽,阻止默认跳转 if (Math.abs(e.clientY - pos.startY) > 5) { e.preventDefault(); e.stopImmediatePropagation(); } }; const onMouseMove = (e) => { isDragging = true; window.scroll(0, pos.top - (e.clientY - pos.y)); }; const onMouseUp = (e) => { isDragging = false; e.target.removeEventListener('mousemove', onMouseMove); e.target.removeEventListener('mouseup', onMouseUp); e.target.removeEventListener('click', onClick); }; const onMouseDown = (e) => { pos.top = window.scrollY; pos.y = e.clientY; pos.startY = e.clientY; // 记录初始点击位置 e.target.addEventListener('mousemove', onMouseMove); e.target.addEventListener('mouseup', onMouseUp); e.target.addEventListener('click', onClick); }; document.addEventListener('mousedown', onMouseDown); });
两种方法对比
- 方法一简单修改现有逻辑,但依赖事件队列时序,若浏览器事件触发间隔有差异可能失效;
- 方法二通过物理位移判断操作意图,更精准,能彻底区分“点击”和“拖拽”,推荐使用。
内容的提问来源于stack exchange,提问作者Simon Ferndriger
相关产品推荐
相关产品推荐

