Home > OS >  JavaScript DOM click handler: added too fast, how to solve without adding a timeout?
JavaScript DOM click handler: added too fast, how to solve without adding a timeout?

Time:09-07

I have a class that represents action to be applied to a fullscreen menu element this.menu.

Looks like this (most of the code omitted). Key point is clickOutsideHandler listener that will check if this.menu contains the event target node: if not, menu should be closed:

class Menu {

  clickOutsideHandler = e => {
    if (!this.menu.contains(e.target)) {
      this.close();
    }
  };

  open() {
    // Apply style changes

    // Add an event listeners
     document.addEventListener('click', this.clickOutsideHandler);
  }

  close() {
    // Revert style changes...

    // Remove event listeners
    document.removeEventListener('click', this.clickOutsideHandler);
  }
}

The problem here is that this.clickOutsideHandler is added "too fast" and even the trigger click get's intercepted by the listener, before actually show the menu:

document.querySelectorAll('[data-menu-trigger]').forEach(trigger => {
   
   trigger.addEventListener('click', e => {
     e.preventDefault();

     // Call open() on the menu instance
     menu.open();
   });
});

A small timeout before adding the event listener would solve the problem. What other options do I have?

CodePudding user response:

The reason it triggers is because the event is bubbling up the tree until it reaches document which now has the handler.

You could stopPropagation() on the event. That way it does not bubble up.

You could also check if the event target is [data-menu-trigger] and not close the menu.

EDIT:

Without touching the trigger definition parts, there is not much you can do beside working around it with a timeout... Or is there? A timeout would simply delay the application of the handler to after the event got fully handled. We can change things around by checking if there was a delay between the open and the close request...

By adding a property that stores the current time when the menu is open, and checking against that time when we try to close the menu, you could make sure to close only if 10ms or something elapsed between the two events.

Might not be cleaner or anything, but let's you solve it without adding a timeout and touching the code you don't want to change.

  • Related