jquery/jquery

Add support for passive event listeners

RByers opened this issue · 65 comments

Blink has shipped support for EventListenerOptions and we expect to soon ship support for the passive option.

Ideally jquery users would be able to mark event listeners as passive, so they can get the same performance benefits. Also @scottgonzalez mentions it would be good for jquery to polyfill the capturing API as well.

Thoughts? Feel free to file any issues / questions with the spec here.

What kind of support are you looking for?

I think it would be really hard to expose either capture or passive via the classic .on() API which has been built with the assumption (by both us and users) of a single multiplexed native event handler. Special event hooks can take advantage of being able to capture events before bubbling jQuery handlers see them. All of jQuery's DOM manipulation knows about jQuery events so it can clone them and/or do teardown when the elements go away.

We could create a new API which is what #1735 is about, but there are several hard issues to solve if we want that to be the foundation for .on(). If these use cases are limited to scroll scenarios it seems like a plugin might be the best approach for now. That way we can avoid solving problems that don't (yet) exist for the general case.

Also, what is a reliable way to check for passive support? Do all non-passive implementations reliably throw an exception if they see an object there instead of a boolean? Are there (or might there be in the future) implementations that support { capture: true } but just ignore other options they don't understand?

What kind of support are you looking for?

I don't presume to know enough about event handling in jQuery and the typical usage patterns to have a valuable opinion on what the right design is. But at a high-level, the thing you want for maximizing scroll performance is some way for the code attaching the listener to disable the use of preventDefault.

This could even be just more syntax for the event name list. Eg. some knob that lets developers opt-in to passive-by-default for touchstart and touchmove events (wheel is currently harder due to the lack of equivalent to touch-action), then some escape hatch if necessary like .on("touchstart:withPreventDefault", handler).

Or maybe developers just have to resort to addEventListener if they want to opt-in to the performance benefits. I've talked with the developers of one popular library already who want this perf benefit and were OK (but not thrilled) about changing their .on() usage to addEventListener in order to get it. I reached out to @scottgonzalez a couple weeks ago (he's been part of the discussions over the past year about this problem / API in the pointer events working group) and he suggested it was worth filing an issue here to discuss.

Also, what is a reliable way to check for passive support?

The explainer shows the typical (if obscure) pattern of feature-detecting dictionary members:

var supportsCaptureOption = false;
document.createElement("div").addEventListener("test", function() {}, {
  get capture() {
    supportsCaptureOption = true;
    return false;
  }
});

I realize the API is tricky because of the data option that already exists. But I'd like the team to start by discussing how to embrace future native APIs. I'm very much in favor of adding this, but in general my requests for embracing new APIs quickly has been met with a lot of pushback and/or bikeshedding.

As far as an actual, usable API, my ideal solution would be to drop data (because it's silly) and add options (because it's useful). The native API is .addEventListener(type, listener, options). We could easily mimic that with .on(type, selector, listener, options). And since deprecating data probably won't happen, we could implement .on(type, selector, data, listener, options). Yes, it will suck to parse those options (perhaps we'll reach a new level of parameter hockey within jQuery), but it's doable since listener is required and acts as a delimiter between the two optional object parameters.

But again, let's start by limiting the discussion right now to whether this feature should make it into jQuery.

This could even be just more syntax for the event name list. Eg. some knob that lets developers opt-in to passive-by-default for touchstart and touchmove events (wheel is currently harder due to the lack of equivalent to touch-action), then some escape hatch if necessary like .on("touchstart:withPreventDefault", handler).

You can get pretty close to that today with special events: http://jsbin.com/bupesajoza/edit?html,js,output

The caveat is that all events attached to a specific element will behave like the first event, whether you provide the class or not. So it works fine for elements where there is only one touchstart handler per element.

I realize the API is tricky because of the data option that already exists.

The extra arg to the public API is messy but it's the least of the problems. I didn't even mention it because solving the architectural issues and legacy expectations would be much harder. If you shoehorn capture and passive into the existing API you have to define how it interacts with all the existing plumbing, worrying about how much it will break. If you create a new API you start with an empty slate and can say, for example if you want, that these events are never cloned or internally tracked during manipulation and also not subject to the special events system or jQuery's special triggering logic.

You can get pretty close to that today with special events: http://jsbin.com/bupesajoza/edit?html,js,output

Interesting, thanks.

Anyway I'll leave it to you jQuery experts to figure out what, if anything, you want to do here. Let me know if there's any questions about or feedback on the API / browser behavior. When we ship passive support in Chrome (currently targeting the upcoming m50 release), you can expect to see a bunch of evangelism with hard data about the performance benefits seen in practice. I can believe it's premature to worry about it much until then, given that early adopters have some OK options for working around jQuery here.

Thanks for opening this. I'd like to see the numbers on the performance benefits before implementing, tho.

Plumbing-wise, this seems very feasible (just extra data to pass to addEventListener and some extra code in jQuery.Event.prototype.preventDefault) and degrades gracefully (worse performance, and successful effects from calls you promised not to make).

Porcelain-wise, five arguments is ugly but @scottgonzalez's suggestion works for extending the .on( events [, selector ] [, data ], handler ) signature. However, it doesn't work as well for the .on( events [, selector ] [, data ] ) signature, for which passive-but-not-data-bound handlers will instead have to look like .on( events [, selector ], undefined, options ) (explicitly undefined data).

Still, I think it can work. +1 from me.

Plumbing-wise, this seems very feasible (just extra data to pass to addEventListener

It's a lot more plumbing than that if we support capture. So let's not consider capture in this proposal at all.

and some extra code in jQuery.Event.prototype.preventDefault

The way I understood @RByers document, if you attach with passive: true then .preventDefault() is just ignored; perhaps a message goes to the console saying you made a mistake. So we can just pass it through and let the browser yell at the user. Also since passive seems to be strictly a performance optimization we should probably just let people set the flag even if it's not supported?

Porcelain-wise

This is probably a relatively rare need, correct? Whatever the API to convey the new data, it doesn't necessarily have to support all signatures or options. It could be a new jQuery.EventOptions object in the data position that we could check for with instanceof to eliminate ambiguity about what it is.

It's a lot more plumbing than that if we support capture. So let's not consider capture in this proposal at all.

Yes. To make it absolutely explicit, this ticket is purely limited to passive: true.

if you attach with passive: true then .preventDefault() is just ignored; perhaps a message goes to the console saying you made a mistake. So we can just pass it through and let the browser yell at the user.

I'm pretty sure we also need to skip our own preventDefault to avoid lying about isDefaultPrevented.

Also since passive seems to be strictly a performance optimization we should probably just let people set the flag even if it's not supported?

Yes. That it doesn't work would only be visible when people call preventDefault, which the passive flag is an explicit promise to avoid.

This is probably a relatively rare need, correct? Whatever the API to convey the new data, it doesn't necessarily have to support all signatures or options. It could be a new jQuery.EventOptions object in the data position that we could check for with instanceof to eliminate ambiguity about what it is.

Please let's not do that. It would be clumsy for both us and our users, and the introduction of an inheritance-based pattern that is decidedly un-jQuery.

Rick released a comparison video showing the potential of passive event listeners on CNN.com and it's pretty compelling: https://www.youtube.com/watch?v=NPM6172J22g

I'm doing some deep dives of CNN and similar websites (e.g. bloomberg) to identify candidates for Passive Event Listeners but early take suggests that having Passive event listeners supported in jQuery would really help a lot.

Is this fast-tracked on several major browsers? We can add plumbing here but it seems like it would be of limited use unless enough browsers support it. Some links to browser tickets tracking implementation would be helpful.

Tracking bug for Safari is here. They've told me they're interested and they've contributed to the design, but who knows if/when they'll make implementing it a priority. Gecko tracking bug is here. Again priority isn't clear.

We're hearing a bunch of demand from web developers though (mostly in response to my video tweet). So there's a bit of a chicken and egg problem here. I think the other engines are waiting to see the extent to which developers adopt this, and many of the developers we talk to don't have an easy way to use it because jQuery doesn't expose it yet. But some of the developers we're talking to aren't using jQuery and can get the benefit right away (and we've got the metrics to let us report what perf improvements such sites are having in aggregate), so that'll probably fuel interest.

Just to be clear, the plugin that I mentioned above already lets you take advantage of passive event listeners, as long as you always want all the events of a particular type on a particular element to either be passive or non-passive. If you aren't ever attaching cancellable touchstart events on a page you can remove the .noPreventDefault class check completely and force all of them to be non-cancellable.

What more do you need at the moment to be able to use passive event listeners with jQuery? The lack of an .on() implementation shouldn't be a blocker. Describe your use cases and we can see what we can do to get you going.

The DOM spec is making increasing use of event listener options (passive late last year, invoke-once last month, and delegation likely soon). I think we should get on board with the trend by adding a final options argument to .on in 3.1, deprecating selector and data and opening an avenue by which passive could be expressed (though actually implementing it would require a significant amount of complexity in registering and updating our run-all-the-things handler).

(though actually implementing it would require a significant amount of complexity in registering and updating our run-all-the-things handler)

That's the messy part and a major change, which is why I'm looking for a way to satisfy this sooner than jQuery 4.0 (or later). We'll need at least a separate addEventListener() per unique combination of options+capture, and at that point it becomes easier to just do one aEL() per jQuery event, especially once we land @jbedard's lower-overhead jQuery.event.fix() code. That will most likely change our private-not-private internal data structure, which is so well known it is used by dev tools so they can report the jQuery handlers for an event.

Just to be clear, the plugin that I mentioned above already lets you take advantage of passive event listeners, as long as you always want all the events of a particular type on a particular element to either be passive or non-passive.

Thanks Dave, I agree this looks like it should be enough to ensure people aren't blocked. I'm reading up on JQuery's event customization support to better understand it, but in the interim can you clarify what would happen if some code did both $(document).on("touchstart.noPreventDefault", handler) and $(document).on("touchstart", handler)?

I'm thinking this pattern is probably even better supported in a framework agnostic way (perhaps there's no need for a jQuery-specific plugin). Eg. in WICG/EventListenerOptions#39 I propose creating a generic library which just fires events named touchstarted and touchmoved (itself implemented on passive listeners). Or we could even go as far as WICG/EventListenerOptions#38 and have a library which makes all touch listeners on a page passive by default.

Actually, given jQuery's push on pointer events maybe touchstart and touchmove listeners added by jQuery should always be passive and jQuery users should just be required to annotate special elements on their page with touch-action - exactly as for pointer events? Even in the short term you could perhaps have some global option to enable this new "touch doesn't block scrolling" mode, as a stepping stone to the full transition to pointer events. @scottgonzalez wdyt?

Describe your use cases and we can see what we can do to get you going.

I think we really need to hear from some end developers using jQuery who are trying to get the perf benefit of passive touch listeners in their code. /cc @tylerbrandt from Optimizely and @CRHain88 who works on CNN.

We're using a third-party library for an image carousel, which is using jQuery to manage touchstart, touchend, touchmove, and touchcancel. If we were to use native javascript, we'd have inconsistent implementation of how to create event listeners. I think we all agree that's not ideal. It also gets messy when a touchmove and mousemove listener need to do the same thing.

How would touchstart.noPreventDefault play with the ability to create namespace events? I like @gibson042's idea of adding an options argument. It seems like it would closely resemble and match the native implementation.

Is that enough information re: our use case?

How would touchstart.noPreventDefault play with the ability to create namespace events?

You can have multiple namespaces on an event, if that is what you're asking. So the third-party library would just need to include the plugin above and add .noPreventDefault to it. Or, someone could write a shim for .on() that takes the extra arg and does essentially the same thing. No matter the API approach, for now the discussion above regarding no mixing of event types still applies. That is, the first one to attach a jQuery touchstart event on a specific element defines whether it is noPreventDefault. You can't mix both on the same element.

@RByers in the interim can you clarify what would happen if some code did both $(document).on("touchstart.noPreventDefault", handler) and $(document).on("touchstart", handler)?

If they were done in that order, the second handler would also ignore preventDefault since the two handlers would both be multiplexed off the addEventListener created by the first one. If they were done in reverse order, the .noPreventDefault would have no effect.

Even in the short term you could perhaps have some global option to enable this new "touch doesn't block scrolling" mode

Any flag that globally changes behavior generally becomes a nightmare for plugin developers because they can't tell what behavior to expect.

@arschmitz Does jQuery Mobile have calls to event.preventDefault() that originate from touchstart or touchmove? I expect if there are any, they'd be indirect uses through vmouse, but any existence at all would be problematic for a global flag.

I feel like my position has been misconstrued. I @scottgonzalez proposed that the interface for adding passive listeners would be .on( eventTypes, listener, { passive: true } ) (and I observed that the options parameter would be valuable even without passive support, since it better matches addEventListener(type, callback, options)). I would not be willing to tolerate observable effects that depend upon registration order, though—we could reattach passive dispatchers upon non-passive listener registration, or maybe automatically go passive by event type, or employ some other solution, but must not privilege the first registration.

@scottgonzalez jQuery Mobile does not call preventDefault in response to touchstart/move older versions do but not current ones. HOWEVER... we are working on adopting hammer.js to replace our gesture events which polyfills touch-action to an extent and calls preventDefault to prevent scrolling where needed. Hammer does not use jQuery any longer so won't be an issue but used to in v1.x

I would really like to see support added for event listener options / passive events in general though jQuery Mobile has always battled issues with scrolling performance on android devices and this would directly help some of our cases.

I have no problem writing and maintaining a plugin if need be as @dmethvin suggested for use in mobile if need be but would really prefer to just see it in core.

This can all be done but it involves a major rewrite of the event system with breaking changes because of the way it affect internals. I don't see that happening until at least jQuery 4 and who knows when that will occur. I am looking for ways to satisfy the need to access passive event listeners via jQuery sooner than a year or more from now.

I would not be willing to tolerate observable effects that depend upon registration order, though

That should definitely not happen in a correct full implementation of all the native options. I like your suggestion above that turns .on() into a polyfill essentially, assuming the spec gets far enough that we don't get screwed by shipping something that changes later. That again points to a longer delay in delivering this.

Any flag that globally changes behavior generally becomes a nightmare for plugin developers because they can't tell what behavior to expect.

Right, there already is a global behavior of doing what addEventListener is doing which makes blocking the default. It just takes a few lines of code like the plugin above to make non-blocking always be used if that's what people want, again with the caveat that the quick solution I mentioned above can't mix options on the same event+element combination. I think there should be a way to do it with more complex special-event-hook code, essentially defining a new touchstart-unblocked event if that's needed, but I can't tell from the discussion so far whether that's needed for the current use cases.

Why not just introduce a new syntax to the events?
$('#elem').on('click.namespace:passive', function() {
// hello
})

I am very much opposed to introduce new jQuery-specific syntax for standardized behavior.

mgol commented

I agree with @gibson042, we should try to align with the spec as much as possible, cutting corners only where the browser APIs are clumsy.

I see your point, but that'd be one alternative to more ground breaking changes.

This is also not future proof. There will be non-Boolean options.

Where is support for useCapture being tracked?

So let's not consider capture in this proposal at all.

So I guess it's not this one.

I've seen #1735 mentioned from quite a few other issues as if it was meant to provide some kind of support for useCapture option, but I don't get how that's related.

One thing we certainly need is just to have a useCapture parameter in on() and related methods, just like the useCapture parameter for addEventListener. I don't care about new completely different APIs. So, is such a feature already tracked somewhere? I have seen a few comments about how difficult it would be to implement, but I don't see any possible argument against the fact that it simply has to be done.

@teo1978 supporting capture or passive properly needs breaking changes which can't happen until 4.0. As I mentioned above it's not just a case of adding a parameter to the .on() API because all of the event plumbing below the .on() API assumes a single multiplexed handler and only supports bubbling. If we're willing to have some pretty serious breaking changes in 4.0 we can get .on() to support these directly in which case I would say we should not bother with gh-1735.

@teo1978 supporting capture or passive properly needs breaking changes which can't happen until 4.0.

I understand all that. The question remains: which is the main feature request / issue that tracks it? Is it this one? I'm talking about capture, not passive events (which I don't even know what they are).

As I mentioned above it's not just a case of adding a parameter to the .on() API because all of the event plumbing below the .on()

I understand that from an implementation point of view it's not just a matter of adding a parameter, it requires a lot of work, and apparently breaking changes. But as far as the API is concerned, it would be just a matter of adding a parameter, wouldn't it?

If we're willing to have some pretty serious breaking changes in 4.0 we can get .on() to support these directly

"If"? Is there any other sensible option?

By the way, by breaking changes do you mean changes that would break currently working applications that use jquery? Could you make an example that explains why that would be unavoidable? I do understand that it implies an enormous rewrite effort (I don't quite understand how this was not taken into account from the very beginning: did you think that one could just live without capture events??), but does it really have to break bc?

I started writing a reply @teo1978 but it's way too large to put into this ticket. Take a look at https://github.com/jquery/jquery/wiki/jQuery-4.0-Event-Design and gh-3127. The changes I have made so far are the "easy" ones.

However, this is not the right place to discuss this further. I don't think we need to create further tickets until it's clear gh-3127 can actually work.

.with({ passive: true }).on(...)

use throttle for resize, mousewheel, scroll events... they are the same but work everywhere.
I used http://underscorejs.org/#throttle and http://amplifyjs.com/api/pubsub/ to reduce the number of events fired bigtime.
It really isnt so hard to implement that into your code....
and good thing with pubsub is that you can have multiple items "listen" to one e.g. scroll handler.
So no need for 2 handlers anymore

Throttling certainly is a big improvement, but the browser still needs to call a JavaScript function for every rendering frame. Even a small function (like a throttle check) will reduce FPS rate considerably when hooked into a blocking scroll event.

For me .with({ passive: true }).on("mousewheel" ... produces Uncaught TypeError: $(...).with is not a function

mgol commented

@ChildishGiant this was just an API proposal. This ticket is still open, meaning there is no support for passive event listeners at the moment. You need to wait for the 4.0 release.

Wish the W3C spec gave a way to specify default event options globally by type.

It's kind of silly when a majority of touchstart events dont use event.preventDefault(), but they aren't willing to change the default behavior. I get they have to support legacy expectations for touchstart, but it's still a bit funny. Now the majority of touchstart handlers have to feature detect new eventOption support and standard libs have to be patched to support this low level API change.

Compounding the issue--in recent chrome versions standard touch detection ('ontouchstart' in document.documentElement) returns true for all windows 8/10 browsers, meaning this whole mess can't be avoided by feature detecting and avoiding touch events.


Anyways, grievances aside, I'm looking forward to a solve for this in 4.0. Having these kind of chores abstracted in libraries like jQuery saves so much headache.

This solve the problem to me:

jQuery.event.special.touchstart = {
    setup: function( _, ns, handle ){
        if ( ns.includes("noPreventDefault") ) {
            this.addEventListener("touchstart", handle, { passive: false });
        } else {
            this.addEventListener("touchstart", handle, { passive: true });
        }
    }
};

Code mentioned by @sergiorighi above, for me only working when i do with direct event binding, for example: $('.target').on('click', handler);

But when do indirect event binding like $(document)on('click', '.target', handler); Seems not working. Any idea for indirect event binding?

For now you would need to attach your own event handler to document and do delegation yourself. by seeing if any of the elements in the path from event.target have the class .target.

@dmethvin Thank you for your suggestion. I am trying to create custom event delegation but can't really close to what jQuery has :). I made a plunker demo: https://plnkr.co/edit/so8Sur?p=preview, i also post a question at stackoverflow regarding this: https://stackoverflow.com/questions/48668380/javascript-custom-event-bind-delegation-like-jquery-on. Can you please give me some light about jQuery event delegation. Thanks in advance.

@bayucandra StackOverflow is the right place for those discussions. This ticket is for tracking the feature.

I'm trying to follow @sergiorighi and @bayucandra. I have this warning

[Violation] Added non-passive event listener to a scroll-blocking 'wheel' event. Consider marking event handler as 'passive' to make the page more responsive.

Caused by this line of code:

$('#screen').on('wheel DOMMouseScroll', (e) => {.........});

Where do I need to put { passive: true } to remove the warning? Thanks.

// Only use addEventListener if the special events handler returns false if (!special.setup || special.setup.call(elem, data, namespaces, eventHandle) === false) { if (elem.addEventListener) { var supportsPassive = false; try { var opts = Object.defineProperty({}, 'passive', { get: function () { supportsPassive = true; } }); window.addEventListener(type, eventHandle, opts); window.removeEventListener(type, eventHandle, opts); } catch (e) { } elem.addEventListener(type, eventHandle, supportsPassive ? { passive: true } : false); } }

Fixed it for when SEO runs on google light house, but reverted due to concerns about wider effects.

What is the status on this?

mgol commented

Who is this issue? Will passive be added?

Is this implemented? Lots of other frameworks are using it, why taking so many years to jquery implement it? If it is still not implemented, is there some "hack" I can use with the .on() to allow for passive?

Sorry for being stupid.

First of all, i can clearly understand where you are coming from and totally appreciate the work being poured into maintaining this library.

But, jQuery is meant to ease developer's life and many enterprises are still using this library and what we expect is consistency across browsers.

I am going to ask people to refrain from posting in this ticket until they have read the discussion above and understand that this feature cannot be implemented without serious breaking changes.

Breaking changes or not - this is a must-have feature for jQuery to continue to exist. If Google are warning website owners that non-passive listeners are bad (especially on scroll listeners) then it's only a matter of time until website owners start questioning their developers.

What are the developers are going to say?

"We can't help it - it's how jQuery works and their team are claiming it's too much work to fix it" ?

As both a business person and a developer I can tell you, with some certainty, that most business owners are likely to tell their developers to find an alternative... and with the likes of VueJS, and with native JavaScript getting better all the time there are plenty of alternatives around once developers are pressured into dropping jQuery.

Once businesses stop buying themes and sites that rely on jQuery, those developers will stop using jQuery too.

By not providing a solution to this jQuery is in danger of becoming obsolete.

@hades200082 you took the words of my mouth! Sinceraly, I am ditching jquery on many projects, not only because of this scroll thing (that is veery important) but because of its lack to use much better and more efficient native browse selectors of the most modern browsers.

jquery was once a great library, nowadays it's just getting annoying with so many limitations.

mgol commented

@hades200082

"We can't help it - it's how jQuery works and their team are claiming it's too much work to fix it" ?

No one is saying that. It’s just that this requires big, potentially breaking changes so it can only happen in a major update. And it is planned for jQuery 4.0. I’m not sure what you’re trying to achieve with this comment.

@batata004

Sinceraly, I am ditching jquery on many projects, not only because of this scroll thing (that is veery important) but because of its lack to use much better and more efficient native browse selectors of the most modern browsers.

I’m not sure what you’re claiming but jQuery uses native querySelectorAll when it works.

@mgol Just google a little bit and you will see how jquery performance is much slower when compared to native browser performance for many types of selectors.

We have this on the roadmap for 4.0 and abusive comments don't help. Just a few comments up I asked people to refrain from non-productive comments but it hasn't helped. If it continues we can just lock the ticket I guess.

Given the comments, I think this ticket has turned into a Google Search destination for people wondering why some [Violation] message shows up in the Chrome console. I know it's frustrating to have Chrome giving warnings like this. In many cases it does not even affect the usability of a site. The Chrome warning is only saying that it may affect performance, not that it does.

If your site is actually having performance issues that require passive listeners to fix, I already posted a simple fix above that will do that without the need for other breaking changes that will come with the 4.0 release. That plugin could be simplified to force all touchstart listeners on the page to passive. It would just go right after loading jQuery.

jQuery.event.special.touchstart = {
  setup: function( _, ns, handle ){
    this.addEventListener("touchstart", handle, { passive: true });
  }
};

With this in place, if you try to call event.preventDefault() inside the handler Chrome gives you a warning that your passive listener can't do that, which means you really needed a non-passive listener after all. Now you'll need to figure out why. Whatever the reason, it's not jQuery.

For more complicated cases where you need a mix of passive and non-passive listeners, 4.0 will be the ticket. Note that existing jQuery plugins that will inevitably break, and you will need to update them. If you are maintaining a web site and haven't written all the code yourself, be prepared for that. It may involve contacting the creators of jQuery plugins for any tooltip, carousel, lightbox, and maybe even browser add-in tools.

"We can't help it - it's how jQuery works and their team are claiming it's too much work to fix it" ?

The problem isn't that mixing passive and non-passive listeners is too much work to fix in jQuery, although it is a major change. The problem is that it may end up being too much work to fix on your web site. There are no touchstart events attached inside jQuery itself so every one of them generating these warnings is somewhere else outside the jQuery library. When we add the ability in 4.0 to mix types the default will still be non-passive listeners so any performance problem won't disappear until you fix either the plugins you depend upon or your own code. I foresee another round of abusive comments once 4.0 ships, this time aimed at plugin authors.

When is 4.0.0 due for release?

The reason you're getting so many visits and comments are because Google is now using Lighthouse as one of their main checks for a site's performance. Since Lighthouse is built into all WebKit browsers, developers are able to run it for themselves and one of the top things that have an effect the performance score in Lighthouse (and therefore a sites Google ranking) is not using passive listeners ... especially for scroll listeners.

Additionally, non-passive scroll listeners are horrible and cause janky scrolling on many sites that use them.

Now... when you combine this with the fact that many CMS (including Wordpress, Kentico and others) use jQuery by default.... as do many Wordpress themes... then you end up in a situation where a lot of websites are starting to drop in Google rankings due to poor performance.

Naturally, those websites' owners are poking their developers asking "why?" and the developers are doing their research and identifying the points from the Lighthouse reports as being contributing factors.

Digging a little deeper they discover that jQuery event listeners are not using passive and start searching around for a solution, invariably leading them here.

As I said, it's unlikely that a 4.0 release is a panacea for web site developers. Even after its release they will need to change additional code, very often it will be code that is part of their theme, or a third-party plugin that they do not understand. It will be hard. Most people will not do it. Most sites are still using jQuery 1.x which will never get a fix for this. However, the code snippet above might even work on jQuery 1.12--I haven't tried it.

If you are one of the people complaining, use the solution I showed above and see if it makes Lighthouse happy.

With the addition of passive listeners there's going to be a requirement for developers to make changes even if sites are run on native JS.

The problem with jQuery at present is that there is no way to make those changes where there is a mix of listeners.

Surely it would make sense to add an optional parameter to the relevant APIs to take a simple boolean value to indicate that we need a passive listener? this way developers could update their code without having to drop jQuery.

We understand this is an important feature. The feature is coming and it's planned for 4.0. Most of the comments here aren't relevant to the development of the feature, so I'm locking the ticket. The workaround Dave posted will allow users to take advantage of the passive option until 4.0 is released. We can't say for sure when 4.0 will be ready. We are all volunteers that work on jQuery when we can make the time, but we'll go as fast as we can.

mgol commented

I added the Behavior change label as it's unlikely to be possible to implement without any breaking change.