DOM Listener: capture, passive, and once

The DOM EventTarget interface is updating its overloads, accepting an arbitraty EventListenerOptions dictionary, an object with a capture and passive properties that indicate if the event should be registered for the capture phase, and if it should not be able to prevent browser default behavior, and accepting also an arbitrary AddEventListenerOptions dictionary, which is an extension of the EventListenerOptions with an extra once property which, if explicitly true, will register the listener once and it will automatically remove it.

What is capture about?

This is the easy one, instead of using true as third argument of node.addEventListener(type, handler, capture), you now can pass an object with such property which is false by defaut: node.addEventListener(type, handler, {capture: true}).

What is passive about?

Like capture, passive is also false by default, and it makes event.preventDefault() a pointeless operation. This is apparently a way to boost up preformance as shown in the following video after using node.addEventListener(type, handler, {passive: true}):

What is once about?

Regardless the listener has capture and/or passive listeners, once will simply flag such listener as “one off“ operation.

document.addEventListener(
  'DOMContentLoaded',
  improveAllTheThings,
  {once: true}
);

There are many “one off“ cases such load, unload, even click for those lazy “first time click“ operations and many more cases, so this is probably the best option we could have natively implemented by specs.

Things to be careful about

The best part of DOM events is that if you set twice by accident the same handler, and within the same capture/bubbling condition, such event will be dispatched only once: there are never duplicated listeners, like it is for instance in node.js or other listeners like implementations.

However, if you dom.addEventListener(type, handler, true) and you also dom.addEventListener(type, handler, false) the handler will be triggered twice, once per phase.

Same is if you dom.addEventListener(type, handler, true) and later on you just dom.removeEventListener(type, handler), assuming removing a handler registered for the capture phase will be dropped if removed for the bubbling one.

In few words, if you add an event via dom.addEventListener(type, handler, {capture: true, passive: true}) you also have to remove it via dom.removeEventListener(type, handler, {capture: true, passive: true})

The lucky bit of this new API is that you don’t need to hold somehow the event listener descriptor, so two fresh new objects with the same propeties will do.

Not fully backward compatible

Creating backward compatible standards is a complicated matter and recycling a third, mostly unknown or not-used, argument was probably a good choice. However, if you set an object in the wild it will be most likely interpreted as true so that node.addEventListener(type, handler, {once: true}) will inevitably happen on the capture phase, instead of the default bubbling one you might have been expecting. This means that is not, in my opinion, a good idea to use these new features without a proper normalizer … but thankfully, that’s not a problem.

DOM4 lib to the rescue

Tested down to my “Jurassic Lab“, an array of devices that never received an OS update in the last 5+ years, the latest version of my dom4 polyfill brings in these latest features and much more.

Jurassic Lab

The main difference with the Chrome focused proposed polyfill, is that dom4 library doesn’t use an expando property at all, the whole relation is based on a WeakMap, polyfilled for real though same events even on old browsers that never supported WeakMap.

How“ you ask? Well, using the implicit weak relationship betweeen nodes and handlers, if specified as object.

// standard way to create a weak relationship
// between a node and a generic object
document.addEventListener(
  'click',
  {
    handleEvent: function () {
      alert(this.name);
    },
    name: 'a weakly referenced handler'
  }
);

Details a part, you can test your browser and verify by yourself if first tests are green it means it works.

Even IE8 ?

Yes, even IE8, all you need to do is to include upfront, and for IE8 only via conditional comments, the ie8 lib.

IE8 dom4 compatibility

Andrea Giammarchi

Fullstack Web Developer, Senior Software Engineer, Team Leader, Architect, Node.js, JavaScript, HTML5, IoT, Trainer, Publisher, Technical Editor