Firefox: Event delegation bug

A Firefox issue where right click on a button results in a click event listener firing because of event delegation performed automatically by a JavaScript SPA framework.

Summary

Firefox dispatches click event from right click of button when delegating on window or document. This can occur even if you don’t realize you’re using event delegation, because it’s performed automatically by many front-end frameworks.

This appears to relate to the following issue: Bugzilla Bug 184051

Expected behavior

Primary click dispatches an event from the clicked element, and secondary click does nothing.

Secondary buttons (like the middle or right button on a standard mouse) MUST NOT fire click events.

Actual behavior

Secondary click on the window or document buttons results in the ancestor event listener firing and an event delegation check matching the button as the target.

Additionally, the event listener on the target element itself is not fired, indicating the event is not dispatched from the clicked button element in a way that is bubbled up through the DOM. I’ve corroborated this through an additional test (listening via an intermediary ancestor) that is not present in the test case for the sake of clarity.

Test case — view the example here

<button class="test-window">
  Test window
</button>
 
<button class="test-document">
  Test document
</button>
 
<button class="test-body">
  Test body
</button>
const buttons = {
  window: document.querySelector('.test-window'),
  document: document.querySelector('.test-document'),
  body: document.querySelector('.test-body'),
};
 
const test = element => event => {
  if (element === event.target) {
    console.log('Ancestor listener fired with matching event target element');
  }
};
 
window.addEventListener('click', test(buttons.window));
document.addEventListener('click', test(buttons.document));
document.body.addEventListener('click', test(buttons.body));
 
const testTarget = event => console.log('Listener fired on event target');
 
buttons.window.addEventListener('click', testTarget);
buttons.document.addEventListener('click', testTarget);
buttons.body.addEventListener('click', testTarget);
Related articles

Beyond allowing the await keyword to be used within them, JavaScript’s async functions have their own useful aspects. As an alternative to Promise.resolve() and Promise.reject(), consider using async functions to cleanly wrap return values in Promises for convenient mocks in your test suite and to ensure consistent return types.

2018
JavaScript
TypeScript

A hidden element within a link can stretch the link’s focus outline to the edge of the viewport — and beyond.

2017
Firefox
HTML + CSS

How to integrate build-time or server-side syntax highlighting for markdown code fences with two libraries: markdown-it and Highlights (Atom’s syntax highlighting engine).

2017
Node.js
JavaScript
Performance