Skip to content
Advertisement

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

I am having trouble getting event listeners to fire when the actual event occurs. I have created a drag-and-drop system that sets a variable hoveringOverTarget = true on pointerover of the target, listens for pointerdown on an image, and then calls a function on pointerup to check if it’s been released over the target.

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));

However, during dragging of the image, no other event listeners respond. The pointerover and pointerleave event listeners from lines 4 and 8 no longer fire when dragging the image in and out of the target zone (despite working fine when not dragging the image), and pointerup function does not fire on pointerup. Rather, nothing happens at all when I release the item, unless the mouse is then moved, even by one pixel, at which point the pointerover event finally fires (hoveringOverTarget is logged as true) and the pointerup event never fires at all.

Through various searching I have seen some people’s problems solved by adding e.preventDefault() to event listeners. When I do this, neither event is ever fired at all.

What is going on?

Answer

Since nobody else was able to figure this one out, I’ll post the changes I made that eventually worked. I tried a whirlwind of different things, but I think my eventual success was mostly due to:

  • attaching the pointerup event listener to the entire document rather than the individual item
  • identifying the dragged item via a document-wide variable, so all functions can reliably access it (rather than hoping e.target was the right item when calling releaseItem(e) from a pointerup listener on an individual item)
  • adding e.preventDefault() to beginItemDrag() which stops the browser’s default image dragging behavior (different than standard element dragging behavior!!!) which selfishly blocks other event listeners during the course of its drag.

tl:dr; I hoisted variables out of the functions and made my event listeners more broad until finally I caught the right events.

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));
Advertisement