JS事件监听器在元素拖拽时失效,仅在pointerup及后续mousemove后触发
问题原因
- 浏览器默认的图片拖拽行为劫持了事件流:当你按住图片触发拖拽时,浏览器会默认启动原生的HTML5拖放流程,生成半透明的拖拽预览,此时原生拖放逻辑会劫持pointer系列事件的传播,导致你绑定的
pointerover、pointerleave、pointerup事件无法正常触发。你遇到的“松开后移动1像素才触发pointerover”的现象,就是因为原生拖拽结束后,浏览器才恢复自定义pointer事件的监听,此时鼠标移动才会触发对应的事件回调。你之前尝试加e.preventDefault()无效,大概率是加错了位置,只有加在pointerdown回调中才能有效阻止原生拖拽行为,加在其他事件回调中反而会阻断正常的事件触发。 - pointerup事件绑定对象错误:你将
pointerup监听器绑定在了被拖拽的item元素上,当拖拽过程中鼠标移出item范围后松开按键,事件无法被item元素捕获,自然不会触发回调。 - 拖拽元素遮挡了目标区域:如果拖拽时你让item元素跟随鼠标移动,此时item会处于鼠标指针和目标区域
.dragtarget之间,触发pointerover的元素会是item本身而非下方的target,因此target的over/leave事件不会响应。 - 事件传参错误:你在
pointerup回调中调用releaseItem()时没有传入事件对象,导致releaseItem函数内部获取e.target时会报错,逻辑无法正常执行。 - 重复绑定监听器:每次触发
pointerdown都会给item新增一个pointerup监听器,多次操作后会造成重复执行和内存泄漏问题。
解决方案
按以下步骤修改代码即可解决问题:
- 在
pointerdown回调中阻止浏览器默认拖拽行为,避免事件被原生拖放劫持。 - 将
pointerup监听器绑定到全局document对象上,确保任意位置松开鼠标都能触发回调,且每次触发后及时移除监听器避免重复绑定。 - 拖拽过程中给被拖拽的item添加
pointer-events: none样式,避免遮挡下方元素导致target的over/leave事件无法触发,拖拽结束后再恢复该属性。 - 修正事件传参,保证
releaseItem能拿到正确的事件对象。
修改后的完整代码如下:
let hoveringOverTarget = false; const target = document.querySelector('.dragtarget'); if(target) { target.addEventListener('pointerover', ()=>{ hoveringOverTarget = true; console.log(hoveringOverTarget); }); target.addEventListener('pointerleave', ()=>{ hoveringOverTarget = false; console.log(hoveringOverTarget); }); } function beginItemDrag(e) { console.log('beginItemDrag called'); // 阻止浏览器默认的图片拖拽行为 e.preventDefault(); const item = e.target; // 拖拽时禁用元素的指针事件,避免遮挡下方目标 item.style.pointerEvents = 'none'; if(target) { // 把pointerup绑定到document上,保证任意位置松开都能触发 function handlePointerUp(e) { console.log('pointerup'); // 事件触发后立即移除监听器,避免重复绑定 document.removeEventListener('pointerup', handlePointerUp); // 恢复元素的指针事件 item.style.pointerEvents = 'auto'; releaseItem(e, item); } document.addEventListener('pointerup', handlePointerUp); } else { console.log('No .dragtarget on page'); item.style.pointerEvents = 'auto'; } } // 直接传入item或者用事件对象获取都可以 function releaseItem(e, item) { // 如果不用传参的方式拿item,也可以从e.target获取,这里两种方式兼容 const targetItem = item || e.target; if(hoveringOverTarget) { console.log('YES!!!'); readcard(targetItem); } } document.querySelectorAll('.item').forEach(item=>item.addEventListener('pointerdown', beginItemDrag));
内容的提问来源于stack exchange,提问作者Henry Floyd
相关产品推荐
相关产品推荐

