Skip to content

Missing machinery for associated Event data #1328

Open

Description

@keithamus

What is the issue with the DOM Standard?

In whatwg/html#9841 (comment) @annevk and I discussed some of the spec around the CommandEvent as part of command/commandfor. The source attribute on the CommandEvent performs logic to retarget the element based on its currentTarget, however while this works in browsers it doesn't work so well in the spec. To quote the conversation there:

@annevk: How does this work when it's initialized synthetically? There's also a wrapping problem.

@keithamus: I am unsure how to answer this, as far as I know this just kind of works? If the currentTarget is an element and its tree-root is not the same as the document then it'll be retargeted to the tree-root host. Therefore currentTarget won't return elements that cross a shadow boundary. Is this not the correct way to express that in the spec?

@annevk: If the source attribute is initialized to something it can't also have a getter. I'm not really sure how to make this work specification-wise. This would require some kind of internal slot, but since those are not formalized it would currently clash with how event constructors are defined...

What is missing is the ability to have associated event data. @annevk mentioned in a DM:

I have an idea of sorts that I think can work. I used to think we needed IDL stuff for this, but we can do this by defining an internal "extras" map on event objects that we fill with these data members.

Hopefully I've captured this properly, and we have an idea of what needs to happen to proceed.

Activity

  1. annevk commented on Nov 25, 2024

    @annevk
    Member

    Thanks! Yeah, this is a longstanding issue where we don't define the internal layout of event objects and instead pretend you can set their public API somehow. Using an Infra map we can fully define the internal layout in a way that's extensible without needing IDL to generate this internal layout for us.

    (And the reason I mention IDL is that I previously thought we'd need IDL for this: https://www.w3.org/Bugs/Public/show_bug.cgi?id=27354#c10. With Infra maps and IDL dictionaries mapping to maps I don't think that's true anymore.)

  2. domenic commented on May 29, 2025

    @domenic
    Member

    Note that this is specifically a problem for properties where we want

    const event = new XEvent({ x: originalX });
    console.assert(event.x !== originalX);

    If we just want some way of associating data that is not directly exposed, or some way of defining getters that don't have counterparts in the XEventInit, that can be done already. (E.g. NavigateEvent does the former.)

  3. domenic commented on May 29, 2025

    @domenic
    Member

    Here is a start of a concrete proposal:

    • All events have an "init properties" map. Its keys are properties of the event interface.
    • We create a new Web IDL extended attribute, in the DOM spec, which auto-generates getter steps.
      • Maybe call it [FromEventInit]
      • The getter steps just return the appropriate value from the event's init properties map.
      • It's fine for non-Web IDL specs to create extended attributes. HTML does it a few times.
    • Fire an event should be updated slightly:
      • Instead of "with its detail attribute initialized to 1", callers should say "with detail initialized to 1". (The important thing is to stop saying "attribute", since we're now talking about how we set the init properties map, instead of how we set the attribute.)
      • All talk of initializing attributes can be updated to talk about changing the init properties map.
    • We should add a helper in DOM for setting values of the init properties map, just for brevity and better abstraction.
      • E.g. "Initialize event's detail to 1 and its screenX to 5."
      • Again, avoid the word "attribute".
    • We update everything in DOM which initializes attributes to use this new abstraction.
    • We update every spec that defines, creates, dispatches, and fires events to use this new interface.
    • Bonus: we can fix "create an event" does not work with required members #600 like so:
      • Instead of step 2 of create an event converting undefined, we can do something more complicated like looping through all the non-required members and converting undefined to their type.
      • We can add an assert in the [FromEventInit] getter steps that the init properties map entry exists. This assert basically fires if the spec author creates an event with required members in its event init dictionary, and fails to set the init properties map.

    In this world, things like ToggleEvent's source would not use [FromEventInit], and would instead define custom getter steps which retarget this's init properties[source]. But otherwise, directly accessing the init properties map would be very rare.

    The con of this approach is that it requires updating basically every spec that uses events. But I think that's kind of hard to avoid.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions