Home > Blockchain >  Make touch and wheel event listeners added by libraries as "passive"
Make touch and wheel event listeners added by libraries as "passive"

Time:01-05

I have two projects:

  1. Bootstrap with jQuery
  2. materialize-css with Vanilla JS

When running a Lighthouse audit on both projects I used to get this warning caused by materialize-css on one project and jQuery on the other project:

enter image description here

I say "used to get", because I did manage to fix it for jQuery simply just applying this workaround:

const opts = (ns) => ... // some code deciding if browser supports passive
$.event.special.touchmove = { setup: function(_, ns, handle) { this.addEventListener('touchmove', handle, opts(ns)) } }
$.event.special.touchstart = { setup: function(_, ns, handle) { this.addEventListener('touchstart', handle, opts(ns)) } }
$.event.special.touchend = { setup: function(_, ns, handle) { this.addEventListener('touchend', handle, opts(ns)) } }

This seem to fix the issue for jQuery, I dont get such warning anymore and everything seems to be working fine.

Now, for materialize-css I found this package default-passive-events that (from docs):

It basically will set { passive: true } automatically every time you declare a new event listener.

Unfortunatelly this library did make materialize components break for touch events because of use of e.preventDefault()...

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

CodePudding user response:

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 patchEventListeners() {
  const originalFn = EventTarget.prototype.addEventListener;
  let supportsPassive = false;
  document
    .createElement("i")
    .addEventListener("i", () => 1, { get passive() { supportsPassive = true } });
  if (supportsPassive) {
    EventTarget.prototype.addEventListener = function(...args) {
      if (
        ['scroll', 'touchmove', 'touchstart'].includes(args[0]) &&
        (!args[2] || args[2].passive === undefined)
      ) {
        args[2] = { ...(args[2] || {}), passive: false };
      }
      originalFn.call(this, ...args);
    }
  }
}
patchEventListeners();
    

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.

  •  Tags:  
  • Related