JS 事件侦听器在项目拖动期间停止工作,仅在 pointerup 和随后的 mousemove 之后触发

JS event listeners stop working during item drag, only fire after pointerup and subsequent mousemove

我无法让事件侦听器在实际事件发生时触发。我创建了一个拖放系统,在目标的 pointerover 上设置变量 hoveringOverTarget = true,在图像上侦听 pointerdown,然后在 [=14= 上调用函数] 检查它是否被释放到目标上空。

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');
    const item = e.target;
    if(target) {
        item.addEventListener('pointerup', ()=>{
            console.log('pointerup');
            releaseItem();
        });
    } else {
        console.log('No .dragtarget on page');
    }
}
function releaseItem(e) {
    const item = e.target;
    if(hoveringOverTarget) {
        console.log('YES!!!');
        readcard(item);
    }
}
document.querySelectorAll('.item').forEach(item=>item.addEventListener('pointerdown', beginItemDrag));

但是,在拖动图像期间,没有其他事件侦听器响应。当将图像拖入和拖出目标区域时,第 4 行和第 8 行的 pointeroverpointerleave 事件侦听器 不再触发 (尽管在不拖动时工作正常图像),并且 pointerup 函数不会在 pointerup 上触发。相反,当我释放该项目时,什么也没有发生,除非 鼠标随后移动,即使移动一个像素,此时 pointerover 事件最终触发(hoveringOverTarget 记录为真)并且 pointerup 事件根本不会触发。

通过各种搜索,我看到一些人的问题通过将 e.preventDefault() 添加到事件侦听器来解决。当我这样做时,根本不会触发任何事件。

这是怎么回事?

由于没有其他人能够弄清楚这一点,我将 post 我所做的最终有效的更改。我尝试了各种不同的东西,但我认为我最终的成功主要归功于:

  • pointerup 事件侦听器附加到 整个文档 而不是单个项目
  • 通过文档范围的变量识别拖动的项目,因此所有函数都可以可靠地访问它(而不是希望 e.target 在从个人的 pointerup 侦听器调用 releaseItem(e) 时是正确的项目项)
  • e.preventDefault() 添加到 beginItemDrag() 以停止浏览器的默认图像拖动行为(不同于标准元素拖动行为!!!),这会在拖动过程中自私地阻止其他事件侦听器。

tl:博士;我从函数中提升了变量并使我的事件侦听器更广泛,直到我最终捕获了正确的事件。

let hoveringOverTarget = false;
let draggedItem = null;
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) {
    e.preventDefault(); // this is CRUCIAL! Disables default image "draggable" functionality
    draggedItem = e.target;
    document.addEventListener('pointerup', releaseItem);
    if(target) target.style.boxShadow = '0 0 20px white, 0 0 40px white';
}
function releaseItem() {
    if(target) target.style.boxShadow = 'none';
    if(hoveringOverTarget) { readcard(draggedItem); }
    document.removeEventListener('pointerup', releaseItem);
}
document.querySelectorAll('.toolboxitem').forEach(item=>item.addEventListener('pointerdown', beginItemDrag));

您似乎找到了一个成功的work-around。不过,以防万一其他人来这里寻找答案,我怀疑这是原始问题的根源:对于允许 direct manipulation (e.g., using a finger to pan or zoom the current page), pointerdown triggers implicit pointer capture 的触摸屏浏览器,这会导致目标捕获所有后续指针事件,就好像它们正在发生一样在捕捉目标之上。所以 pointerleave,例如,永远不会触发。这一直持续到捕获在 pointerup.

之后隐式释放

要解决此问题,您只需在 pointerdown 事件侦听器中释放捕获——此处 beginItemDrag——如果已设置:

if (e.target.hasPointerCapture(e.pointerId))
  e.target.releasePointerCapture(e.pointerId);