Skip to content

preventDefault and stopPropagation in the addEventListener options bag #1378

Open
@nicolo-ribaudo

Description

@nicolo-ribaudo

What problem are you trying to solve?

#1038 proposes a method to create a promise that gets resolved once a given event is fired. For example (ignore the name, I don't think it's settled and I just came up with one):

const img = new Image();
img.src = srcURL;
img.addEventListener("load", () => { /* continue doing something with the image */ });

// -->

const img = new Image();
img.src = srcURL;
await img.eventPromise("load");
/* continue doing something with the image */

One problem with that proposal, that is discussed in its comments, is that it does not work with preventDefault and stopPropagation, because they need to be called synchronously and thus before that the promise resolves.

What solutions exist today?

/

How would you solve it?

In many cases stopPropagation/stopImmediatePropagation/preventDefault are called unconditionally at the beginning of the event listener callback. For those cases, a declarative API would be enough. Assuming that .waitFor("load") would take the same options bags as .addEventListener (only disallowing once, I guess), then it would look like:

await button.eventPromise("click", { preventDefault: true });

With the .addEventListener API, it would be:

button.addEventListener("click", e => {
  e.preventDefault();
  // do stuff
});

// -->
button.addEventListener("click", e => {
  // do stuff
}, { preventDefault: true });

I'm opening this as a separate issue from #1038 just because it would also apply to the non-promise API. It's most useful for the promise-based one, but in general it's nice to have declarative alternative to imperative ones.

I'd be curious to know if it'd be helpful for implementations to know that an event is going to be prevented/stopped before calling the callback.

Context

For the web integration of the AsyncContext proposal, we are exploring the possibility of not propagating the context through async JS-caused events.

However, we received feedback that it'd be highly desirable to propagate it for load/error event, as their listener is conceptually a continuation of whatever started the loading process.

Events/addEventListener are not the best primitive for signaling completion of some operation, which would be best suited by promises. If we can untangle the "I want to continue do something when this intermediate operation completes" use case (e.g. load/error) from the "start this new operation when something happens", we can avoid pushing complexity needed by the first one onto the second.

cc @annevk

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions