Git Product home page Git Product logo

eventlisteneroptions's Introduction

Passive Event Listeners (EventListenerOptions)

This work is now part of the official WHATWG DOM spec. Please file any issues/pull requests there. This repository (and its resources / discussions) are only preserved here as an archive.


An extension to the DOM event pattern to allow listeners to disable support for preventDefault, primarily to enable scroll performance optimizations. See the explainer document for an overview.

Spec changes

Status of implementations:

Additional background on the problem:

Additional resources for understaning and using passive listeners

Issues with and adoption by key libraries:

History:

eventlisteneroptions's People

Contributors

aghassemi avatar bschnelle avatar calyhre avatar domfarolino avatar foolip avatar jakearchibald avatar montogeek avatar oliverjash avatar patrickhlauke avatar paulirish avatar rafgraph avatar raphamorim avatar rbyers avatar tdresser avatar webreflection avatar yoavweiss avatar zamfofex avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

eventlisteneroptions's Issues

Need some mechanism for feature detection

Whether EventListenerOptions is supported at all, and which options in particular are supported.

Maybe an API like EventTarget.supportedOptions that returns an EventListenerOptions (where the values don't mean anything)?

CFC to migrate to the Web Platform WG

It seems that EventListenerOptions has gotten wide backing from browser vendors, with Blink, Gecko, WebKit implementations underway or shipping ๐ŸŽ‰ .

This makes it a good candidate to migrate to the Web Platform WG for formal standardization.

People working on the spec, WDYT? How much more work is needed to migrate this work?

@jacobrossi, I know Edge folks had some concerns, but can the Edge team live with the current design?

Write explainer doc

Now that I've converted the spec into a DOM PR #19, I should to extract out the explanatory text and justification into a simple explainer doc.

Should event timing implications be explicit?

Even though requiresCancelable=false allows scrolling to proceed without blocking on JS, that should not mean that developers will see the touch events at a location/target different from what the user actually touched. Hit testing behavior / observable event ordering must remain the same. This should fall out naturally in chromium due to the design of threaded scrolling and (since hit-testing isn't specified) is probably out of scope for this document. But maybe a note is deserved? It should be easy to create a test page that demonstrates this.

Clarify role of EventListenerOptions in identifying handlers

It's not be entirely clear whether:

   addEventListener(type, handler);
   addEventListener(type, handler, {requiresCancelable: null});

Adds one event listener or two. Similarly, what about:

   addEventListener("scroll", handler, {requiresCancelable: false});
   addEventListener("scroll", handler, {requiresCancelable: null});

Add library which generates "touchstarted" events

Like #38, another library useful for easy adoption would be to expose a new set of events: touchstarted and touchmoved (like the API discussed in #24) or aftertouchstart and aftertouchmove. These would be implemented on top of passive touch listeners (perhaps via a setTimeout(0)). This would have the advantage of making it easy for script consuming DOM events via various frameworks (like jquery/jquery#2871) to opt-in to using passive touch listeners.

Thoughts?

Should preventDefault not be exposed on passive event handlers?

I think this is a continuation of #3. I haven't seen this suggestion made for that problem.

element.addEventListener('touchstart', function(e) {
  console.log(e.preventDefault); // logs undefined
  e.preventDefault(); // TypeError: e.preventDefault is not a function
}, {passive: true});

The error message could be special-cased to add the reason why e.preventDefault is not a function.

Replace mayCancel with blocksScroll?

My original proposal on public-pointerevents suggested a blocksScroll option instead of this mayCancel option. Maybe that would be better after all?

Benefits:

  • Avoids @ojanvafai's concern (#13) about scenarios where a touch handler won't call preventDefault but would still want to block scrolling.
  • Directly describes it's primary purpose and performance consequence
  • Could someday be directly extended to allow a scroll event handler to opt-in to being running in-sync with scrolling (as for scroll-blocks-on)

Disadvantages:

  • Not broadly useful enough to justify changing the core event handler model.
    • How compelling are the other uses cases for mayCancel?
  • Attempting to spec threaded scrolling behavior explicitly may be opening a Pandora's box of spec hassle. Threaded scrolling is otherwise an implementation detail not directly exposed to the platform.

Previously Olli Pettay argued:

Definitely not this. This is not a strong enough case to change an API which has existed for 15+ years, IMO.

And then

So, "I'm not going to call preventDefault()" might be such, useful also in other cases than just with wheel. A bit odd API, but might be useful, really useful.

Anyone else have opinions here?

Request for an Update Demo File to be added to this Repository

First I want to say good work to @RByers for creating this API and the video on CNN Demo is pretty amazing!

I have gone through all this repository, all the issues and all the posts in jQuerry, Modernizr and RByers own Github on this topic. I can see very little information about this API doing a search in Google and from reading all the comments there are so many changes in the way to write the API.

The Demo on RByers Github I notice is using the conversation from Modernizr #1894 which I will include here:

var supportsPassive = false;
try {
addEventListener("test", null, { get passive() { supportsPassive = true; } });
} catch(e) {}

['touchstart', 'touchmove', 'touchend', 'wheel'].forEach(function(type) {
// This handler is demonstrating how to (passively) monitor scroll latency.
addEventListener(type, monitoringHandler, supportsPassive ? {passive:true} : false);
});

I understand the concept you are saying here: "Making Touch and Mouse Events Passive to remove the 100mS delay".

Having read the explainer.md a few times I am still confuse by the order of everything and reading all the comments elsewhere is even worse.

Is it possible for you to add a Demo Example of this API to this repository showing us exactly the correct usage.

Thank you in advance.

Rework spec into the DOM standard

I was sloppy and vague in a bunch of my wording. There's enough consensus on the rough outline here now to define it formally ad a PR to the DOM spec.

In #18 @annevk said:

What would help me if the proposal was instead done as a set of changes to the algorithms in the DOM Standard. Currently the implications to the processing model are rather vague.

Add library to make touch listeners passive by default

Thinking about how to make it easier to get the perf benefits of passive touch listeners, I'm thinking it would be helpful to publish a sample library which makes all touch listeners passive by default unless requested otherwise.

In particular, we could hook addEventListener and set the passive option automatically for touchstart or touchmove unless the target element has a blocksScroll class applied to it or something.

I'm sure there would be scenarios where naive usage of this causes some subtle breakage (eg. carousels that pan without disabling scrolling) but it would probably be pretty minor in practice often easily addressed by the application of touch-action. Such issues are probably better than the alternative of such sites just having scroll jank issues, and likely even better than those sites getting impacted by our intervention (WICG/interventions#18).

Thoughts?

Need some additional text around add/removeEventListener clearly specifying how equality works

Take for example:

var f = function(e) {
};

addEventListener("foo", f, false); /use capture false; mayCancel implicitly true/
addEventListener("foo", f, {useCapture: false, mayCancel: true}); /* this should do nothing; since f is already added with those args. */

Likewise:
removeEventListener("foo", f, {useCapture: false, mayCancel: true}); /* this should remove the first added event listener*/

This slightly differs from the text in http://www.w3.org/TR/dom/#dom-eventtarget-removeeventlistener
as the EventListenerOptions object is likely created and destroyed between add/remove calls so we need to enumerate all the configurations of the options to determine if they are equal. This may be specified somewhere how dictionaries behave; but I haven't found a reference to it.

Polyfill doesn't work in firefox 47

Hi, I was exited about EventListenerOptions, so I started use this polyfill in my project.
It works fine in all my supported browser, but firefox . I don't know exactly why, but it blocks my scripts.

by the way thanks for your job.
marco

Should this repo be set to "archived"?

As work on this has migrated/merged to WHATWG, should we use github's archive feature to make it read-only, and somehow amend the readme to call this out a bit more somehow?

booleans should default to false

It looks like there are already lots of threads covering potential changes to the name and semantics that might impact this. But, wherever they land, they should not end up with a boolean that defaults to true. Web IDL even explicitly warns against this:

Warning

It is strongly suggested not to use default value of true for boolean-typed arguments, as this can be confusing for authors who might otherwise expect the default conversion of undefined to be used (i.e., false).

Swiping horizontally in iOS 10

iOS 10 safari or WKWebView supports passive option, but doesn't support css touch-action: pan-y, neither does Pointer Events.

So is it possible to use passive to swipe horizontally to rotate a carousel, dismiss an item or reveal a drawer, while still permitting vertical scrolling?

passive is ambiguous

When first being introduced to this feature, I assumed that passive events where sort of "read-only" events. Meaning they could not change the DOM. After reading a bit more, I realise it only removes the preventDefault method on events.

Because they only do one thing, I'm suggesting to rename the option : preventable. It defaults to true and you set it to false to get the performance benefits.

bikeshed: mayCancel is a confusing name for some use cases

mayCancel makes total sense with respect to preventDefault, but it also affects whether the event is async with respect to the default action, right?

Here's an example where this name is confusing: a popup that you want to go away before the scroll starts. You still want the scroll to happen, but you don't want the popup to scroll with the content, you want it to just go away on touchstart. I can imagine as a developer being in this situation and trying to figure out if there's a way to get the event to be synchronous. "mayCancel" would not seem likely as a way to fix this.

How about "requiresSync"? or "mayBeAsync"? I don't love those names, but they're a bit more accurate and they're the best I can come up with at the moment. :)

Make it easier to target non-passive event listeners to specific classes

I've been looking at what it would take to get FB to use passive mousewheel events. One challenge is that Facebook currently listens to mousewheel events to manage scroll chaining. For example, if you scroll to the bottom of a chat window we don't want the mosue wheel to cause you to start scrolling the entire document.

We currently do something like this:

document.addEventListener('wheel', (e) => {
  // Find the first parent of e.target that has the css class scrollable
  var scrolling_wrapper = e.target.closest('.scrollable');
  if (!scrolling_wrapper) return;
  // apply scroll only to wrapper
});

In theory, we could attach a scrollable event to all divs that we wanted to have this behavior. But in practice this is hard. Markup might be generated by the server and it may not be easy to ensure this event handler is applied. Ensuring event handlers are added requires CPU time up front to add the event handlers and memory to store the event handlers.

Capturing events on a high level element (such as the document) and filtering them at event time is a common pattern in other parts of our codebase and other codebases.

An extension to EventListenerOptions could meet this use case.

document.addEventListener('wheel', (e) => {
  // Find the first parent of e.target that has the css class scrollable
  var scrolling_wrapper = e.target.closest('.scrollable');
  if (!scrolling_wrapper) return;
  // apply scroll only to wrapper
}, {
  targetFilter: '.scrollable *',
});

targetFilter means "only listen to this event if the target of the event matches this selector when it is evaluated relative to the node to which the event listener is attached". Since not all browsers will support targetFilter a person using the filter must also check it within the listener.

In addition to reducing the number of non-passive event listeners this might make some of the existing libraries and use cases more idiomatic. For example, it would mean that when single stepping through events installed by a library like jquery a user would never see events that the library would just filter out.

Should passive listeners be able to stopPropagation?

Calling stopPropagation isn't a very "passive" action.

I can't think of any immediate performance gain to be had by forbidding calling stopPropagation, but it might have some performance implications for input handling on compositor worker.

i.e., if input is being handled on the main thread and off thread, and all listeners are passive, it would be nice to be able to dispatch input to both threads at once, without having to worry that the main thread might call stopPropagation.

Should the passive option be part of the event listener key

At the moment, the following code installs two listeners (handler will get invoked twice):

  target.addEventListener(type, handler);
  target.addEventListener(type, handler, {passive: true});

@annevk points out this may not make sense - it may be better to treat them as the same.

If we want to change this, we have to define what the second call does. Is it just ignored? Or maybe a listener can be "upgraded" from passive to blocking but not the other way around?

I was really just following the precedent set by capture here - no real argument one way or the other. Can anyone shed light into why capture behaves this way? That seems odd / useless also.

Is there a good reason we'd want capture to have different semantics than passive (and presumably new options)? Maybe capture can just be forever odd for legacy compat reasons?

This is an edge case I think is unlikely to affect any real code, so I think the compat risk of changing this is minimal.

Is modifying the value of cancelable OK?

It's a little weird to say that 'Event.cancelable' changes value in the context of a particular handler. The alternative of an event being uncancelable only when ALL handlers have asked for that seems worse though. I guess we could describe this as the handler getting a mutated copy of the event? But that would be annoying (possibly expensive) to implement and probably impossible to polyfill.

Safe to change behavior of addEventListener(type, callback, object)?

If the third argument is of type (boolean or EventListenerOptions), it seems like this would have to change the behavior of existing calls to addEventListener(type, callback, {}) or any kind of object as the third argument.

This would of course be accidental, but given how many calls to addEventListener are out there it doesn't seem necessarily safe to change. If overloading the third argument still seems like the way to go, the size of the problem could be measured in Blink.

Modernizr.passiveeventlisteners does not exist?

Modernizr.passiveeventlisteners does not exist?

It return undefined in the normal build and I don't seem to see any custom addition I can turn on to get "passiveeventlisteners" in Modernizr.

UAs observing the presence of event listeners is bad

What happens if the UI process/thread thinks that a point only has non-canceling event listeners, but when the event reaches the main thread, a regular event listener that calls preventDefault() has been added? It's too late to actually prevent the scroll, but it'll look to the event listener like it can prevent it.

Perhaps create non-cancelable events when the UI process/thread actually isn't waiting for the result? Though this would make the presence of other no-op event handlers observable, ugh.

Warning keeps showing when using preventDefault?

Smooth scrolling performance is essential to a good experience on the web, especially on touch-based devices. 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. While there are particular scenarios where an author may indeed want to prevent scrolling, analysis indicates that the majority of touch event handlers on the web never actually call preventDefault(), so browsers often block scrolling unneccesarily.

What about when we use preventDefault ? The warning in Chrome should not appear anymore, but it does.

Handling of 'wheel' input event was delayed for 122 ms due to main thread being busy. Consider marking event handler as 'passive' to make the page more responive.

should requireCancelable be an enum?

@tdresser commented on the definition of requireCancelable:

Is there precedent for null being used to indicate the default value in APIs like this? Would an enum be clearer?

I'm not sure. I have a mild preference for boolean since this is really a 2-state switch. Another option might be to make it an optional member of the dictionary that's a simple boolean, rather than supporting an explicit 3rd value.

Invert 'mayCancel' to 'passive', making it consistently false by default

When using the new API (that takes an options dictionary), perhaps mayCancel should be false by default to encourage the more performant behavior? This could eventually become a burden if EventListenerOptions ends up getting used by other things - eg. do we really want to force every mousedown handler to tell us it might preventDefault when there's little performance benefit to knowing that up-front? But I'd love this to be the default for touchstart (may help developers think about the perf implications of asking for a cancelable event).

This would also address #15.

Is preventDefault being silently ignored on uncancelable events too error-prone?

Some have argued that it's too error prone to have preventDefault be silently ignored on uncancelable events. In Chrome I've added a devtools warning to try to help spot these. But we could consider throwing (for this new special case). Personally I think it's more webby to just stick with the currently specified behavior.

Add simpler feature detection mechanism?

There's lots of concern in #12 about the complexity of dictionary-member based feature detection. Perhaps we should be designing something simpler, ideally as a general pattern since dictionary-based APIs are being used / extended everywhere on the web now.

I originally had a getSupportedListenerOptions() API that was easier to use, which we "fixed" in #16. But maybe we should really be thinking of a better pattern? Some have suggested EventTarget.supportsPassive. This is kind of similar to DOMTokenList.supports and CSS @supports.

Suggestions?

Suggestion: add more event types (xxxpassive or xxx-ed) instead of EventListenerOptions

Currently there are only a few event types (touchstart, touchmove, and wheel) really benefit from mayCancel option, so maybe it is not worth to introduce the new concept of EventListenerOptions to all events immediately. Instead, we can simply define touchstartpassive, touchmovepassive, and wheelpassive events, which are the non-cancelable variants. These variants are also available via DOM0 properties (i.e. onxxx).

EventListenerOptions may be added to some new API in future, as suggested in #18.

Update:
"xxxpassive" may be too long names, how about "-ed" names, i.e. touchstarted, touchmoved, and wheelrolled? Past tense implies that those events just happend, too late to cancel.

Rather than overload the 3rd argument of addEventListener could we overload the 2nd?

The 2nd argument to addEventListener is an EventListener callback interface. We could possibly add additional attribute members to this interface. Eg.

callback interface EventListener {
  void handleEvent(Event event);
  readonly attribute boolean mayCancel;
};

Event registration code would then look like:

element.addEventListener(type, {handleEvent: function(e) {
    ...
  }, mayCancel: false});

This would separate capture from mayCancel but it makes some sense since mayCancel is describing a property of the EventListener. This would have the benefit of being backwards compatible (although we'd still want to encourage use of a polyfill to prevent accidentally depending on cancelable events). This would also avoid the need to change any of the language around the capture variable and what constitutes a redundant (and so ignored) event handler registration. However this seems like it might be an abuse of callback interfaces, is there precedent?

Should the API be forwards compatible?

Is the requirement to use feature detection or a polyfill OK?

Olli Pettay suggests:

...but unfortunately that is not backwards compatible. If we could find a nice backwards compatible syntax.
addEventListener("wheel", listener, true, { preventDefault: "never" }); would behave fine, but is a bit long. Would it be good enough?)

To me the benefit of this doesn't seem worth the cost in verbosity, but I could live with it if there was consensus otherwise.

Another option is discussed in #11

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.