What Are Passive Event Listeners

What are passive event listeners?

Passive event listeners are an emerging web standard, new feature
shipped in Chrome 51 that provide a major potential boost to scroll
performance. Chrome Release Notes.

It enables developers to opt-in to better scroll performance by eliminating the need for scrolling to block on touch and wheel event listeners.

Problem: All modern browsers have a threaded scrolling feature to permit scrolling to run smoothly even when expensive JavaScript is running, but this optimization is partially defeated by the need to wait for the results of any touchstart and touchmove handlers, which may prevent the scroll entirely by calling preventDefault() on the event.

Solution: {passive: true}

By marking a touch or wheel listener as passive, the developer is promising the handler won't call preventDefault to disable scrolling. This frees the browser up to respond to scrolling immediately without waiting for JavaScript, thus ensuring a reliably smooth scrolling experience for the user.

document.addEventListener("touchstart", function(e) {
console.log(e.defaultPrevented); // will be false
e.preventDefault(); // does nothing since the listener is passive
console.log(e.defaultPrevented); // still false
}, Modernizr.passiveeventlisteners ? {passive: true} : false);

DOM Spec , Demo Video , Explainer Doc

Consider marking event handler as 'passive' to make the page more responsive

For those receiving this warning for the first time, it is due to a bleeding edge feature called Passive Event Listeners that has been implemented in browsers fairly recently (summer 2016). From https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md:

Passive event listeners are a new feature in the DOM spec that enable
developers to opt-in to better scroll performance by eliminating the
need for scrolling to block on touch and wheel event listeners.
Developers can annotate touch and wheel listeners with {passive: true}
to indicate that they will never invoke preventDefault. This feature
shipped in Chrome 51, Firefox 49 and landed in WebKit. For full official explanation read more here.

See also: What are passive event listeners?

You may have to wait for your .js library to implement support.

If you are handling events indirectly via a JavaScript library, you may be at the mercy of that particular library's support for the feature. As of December 2019, it does not look like any of the major libraries have implemented support. Some examples:

  • jQuery.js - ongoing issue: https://github.com/jquery/jquery/issues/2871
  • React.js - ongoing issue: https://github.com/facebook/react/issues/6436

    React 17 discussion: https://github.com/facebook/react/issues/19651
  • Hammer.js - closed due to no follow up: https://github.com/hammerjs/hammer.js/pull/987
  • perfect-scrollbar - closed:
    https://github.com/noraesae/perfect-scrollbar/issues/560
  • AngularJS - closed due to won't fix: https://github.com/angular/angular.js/issues/15901

Make touch and wheel event listeners added by libraries as passive

First of all, this is just a warning, not an error.



Is there a way, similar to jQuery workaround above, to fix all the materialize-css added event listeners? P.S. It does not use jQuery

Yes, there are, actually, three ways:

  1. Get read of the warning, without improving performance

Simply add { passive: false } as third param to all listeners which don't have an object as third parameter. This will tell browsers that .preventDefault() might be called on those events. However, especially on scroll, touchmove and touchstart events, the performance increase is considerable when the browser knows that the default behavior won't be prevented on an event. When marked as passive, the scrolling will be much smoother and the perceived performance will be significantly increased.


  1. Improve performance by potentially breaking functionality

Add { passive: true } as third param to all listeners which don't have an object as third paramter. This will tell browsers that .preventDefault() will never be called on those events. You'll see a performance increase, but code relying on preventing those events will break.

Note: this is what both the jQuery fix and the default-passive-events package do, btw.


  1. The proper way

The proper way is to go into the source code of whatever lib you're fixing, figure out which events might ever be prevented and add { passive: false } for those, while adding { passive: true } for everything else.

I'd argue finding all the places where an event is prevented in a lib is not a huge task.

You can do this in a fork, ideally PR-ing it back into the lib's repo, for others to benefit, just like you benefit from the lib itself.


Here's solution 1.

function patchScrollBlockingListeners() {
let supportsPassive = false;
const x = document.createElement("x");
x.addEventListener("cut", () => 1, {
get passive() { supportsPassive = true; return !!1 }
});
x.remove();
if (supportsPassive) {
const originalFn = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(...args) {
if (
['scroll', 'touchmove', 'touchstart'].includes(args[0]) &&
(typeof args[2] !== 'object' || args[2].passive === undefined)
) {
args[2] = {
...(typeof args[2] === 'object' ? args[2] : {}),
passive: false
};
}
originalFn.call(this, ...args);
}
}
}
patchScrollBlockingListeners();

The above code only "patches" scroll, touchmove and touchstart events (by declaring them non-passive). this makes the warnings go away, without touching third party code.

Note: in order for this to work, the function has to be run before loading whatever library is throwing the warning. The code above only patches events added after it was run, it doesn't patch already bound listeners.

Note: Solution 2 is the same code, except the passive override is set to true.

Another, rather important, note: while I can't guarantee that it will work for everyone, passing the following to passive has worked for me on "patching" a lot of libs in a lot of projects:

passive: typeof args[2] === "boolean" ? args[2] : true

It respects the previous addEventListener syntax (where 3-rd arg was passive itself, as boolean) and sets it to true when not specified at all. This, however will break events where the passive was not specified and the events are being canceled in some scenario, which is why I haven't included it above.

Unable to preventDefault inside passive event listener invocation

When you use a passive event listener you're making a promise not to use a preventDefault() in that listener to disable scrolling. This frees the browser up to respond to scrolling immediately without waiting for JavaScript. A passive event listener is mainly used in touch start or touch move or wheel listeners. This ensures a reliable smooth scrolling experience, in theory.

So you can either remove the preventDefault() in the event listener or
mark the event listener not being passive but I wouldn't recommend that. Try testing what happens when you remove the preventDefault() and in theory, it should work the same

I hope this helps

How to check if a listener event is a passive event?

You can test the value of event.defaultPrevented after the call to event.preventDefault();. The browsers will throw warnings/errors for the call if it is a passive event, however it doesn't halt execution of the javascript. So the following should work:

document.addEventListener('touchstart', function(event) {  event.preventDefault();    if (event.defaultPrevented) {    // This is not a passive event  } else {    // This is a passive event  }}, { passive: true });

What is the meaning of [Violation] Added non-passive event listener to a scroll-blocking 'touchstart' event in WordPress

If you can access or modify your code, just use highly flexible and configurable passive-events-support package to debug and make event listeners passive without touching 3rd party source code.

See this answer on similar issue:
https://stackoverflow.com/a/70811715/2333299

For me, this package fixed all the warnings cause by materialize and jquery. Hope it helps you too.



Related Topics



Leave a reply



Submit