[csswg-drafts] How to handle addEventListener on `CSSPseudoElement`? (#12163)

danielsakhapov has just created a new issue for https://github.com/w3c/csswg-drafts:

== How to handle addEventListener on `CSSPseudoElement`? ==
Full context: https://github.com/danielsakhapov/CSSPseudoElementDoc?tab=readme-ov-file#41-addeventlistener-on-a-proxyhandle

Description:
Given that the CSSPseudoElement object acts as a persistent handle, attaching an event listener via addEventListener raises further questions. 

* What does it mean to listen for an event on a handle when the actual pseudo element might not be currently rendered (e.g., due to display: none)?
* Should the listener only become active when the pseudo element is actually rendered?
* How does this interact with the retargeting behavior?
* If event.target remains the Element, how does a listener on the CSSPseudoElement get invoked?
* Does it require a special dispatch phase?

This would require some heavy discussions, as it’s already known that authors want to be able to count clicks on ::scroll-marker pseudo elements for analytics purposes.

Proposed variants are included in the table:
| Model                      | `event.target` Behavior (for existing events like `click`) | `addEventListener` on Pseudo? | Pros                                                                         | Cons                                                                                                  | Feasibility Notes                                                                                             |
|----------------------------|-----------------------------------------------------------|------------------------------|------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------|
| A: Status Quo              | `Element` (retargeted)                                    | No                           | Full web compatibility. Simple.                                              | No direct script interaction with pseudo events.                                                      | Current state.                                                                                                |
| B: Full `EventTarget`      | `CSSPseudoElement`                                        | Yes                          | Direct event handling on pseudo. Conceptually clean object model.            | Major breaking change for web compatibility. Complex bubbling definition needed, esp. for non-tree pseudos. | Highly unlikely due to web compatibility constraints.                                                         |
| C: Hybrid (Parent + Selector) | `Element` (retargeted)                                    | No (Interaction via parent)  | Avoids web compat issues. Sidesteps pseudo lifetime/eventing complexity for the use case. | Less direct object model. Requires specific API pattern (like Web Animations `pseudoElement`).           | Proven feasible for Web Animations L1. Potentially applicable to other interactions.                        |
| D: Limited `EventTarget`   | `Element` (for existing events). `CSSPseudoElement` (for new specific events only). | Yes (for specific events)    | Maintains web compat for existing events. Allows direct handling for new use cases. | Complex dispatch logic. Bubbling complexity remains for events targeting pseudo. Potential developer confusion. | Requires careful definition of which events target what, and clear bubbling rules. Avoids the main breaking change of Model B. |

Potential solutions on example of `::scroll-marker`:

* No direct listening on pseudo element (Status Quo):
    * **Description**: Do not allow `addEventListener('click',...)` directly on the `CSSPseudoElement` for `::scroll-marker`. Clicks physically hitting the marker would continue to dispatch with `event.target` set to the originating scrollable `Element`. Developers would have to attach the listener to the `Element` and use coordinate-based hit-testing within that listener to infer if the click landed on the marker area.
    * **Pros:**
        * Requires no changes to the event dispatch model.
        * Zero web compatibility risk regarding `event.target`.
    * **Cons:**
        * Very poor developer ergonomics for this specific use case. Calculating marker positions and sizes to perform manual hit-testing is complex, brittle (breaks with style changes), and potentially slow.
        * Makes the `CSSPseudoElement` object less useful and doesn't leverage its `EventTarget` inheritance.
* Allow direct listening with special dispatch phase:
    * **Description**: Allow developers to call `scrollMarkerPseudoElement.addEventListener('click', counterFunction)`. When a click physically occurs on the scroll marker:
        * Special Phase: The browser's event system identifies the hit target as the `::scroll-marker` and invokes `counterFunction` (and any other listeners attached directly to this specific `CSSPseudoElement` handle).
        * Standard Phase: The event dispatch continues as normal, but crucially, `event.target` is set to the originating scrollable `Element`. The event then proceeds through the capture and bubble phases relative to the `Element`, without re-invoking `counterFunction` during this standard flow.
    * **Pros:**
        * Directly addresses the developer need: allows attaching listeners specifically to the pseudo-element of interest.
        * Preserves web compatibility by ensuring `event.target` remains the `Element` during the standard, observable event flow.
        * Provides a meaningful use for the `EventTarget` inheritance on `CSSPseudoElement`.
        * Much better developer ergonomics than manual coordinate checking.
    * **Cons:**
        * Requires implementing the modified event dispatch logic (the "special phase").
        * The event model becomes slightly more complex internally, though the developer-facing API (`addEventListener` on the pseudo) is intuitive.
* Introduce a new specific event type:
    * **Description**: Instead of using the standard `click` event, define a new event type like `scrollMarkerClick` that only fires on the `CSSPseudoElement` handle for the `::scroll-marker`. Standard `click` events would continue to target the `Element` only.
    * **Pros:**
        * Avoids any ambiguity or modification related to the standard `click` event dispatch.
        * Clearly separates the interaction.
    * **Cons:**
        * Introduces a new event type for a very specific interaction, potentially leading to event type proliferation.
        * Developers might intuitively try to listen for `click` first and be confused why it doesn't work directly on the pseudo-element handle without the special dispatch phase.
        * Doesn't fully resolve the general question of how other standard events (like `mouseover`) should interact with pseudo-elements if direct listeners are desired.

**Soft, but somewhat desired recommendation:**

Option 2: Allow Direct Listening with Special Dispatch Phase.

Justification: This option provides the best balance. It directly enables the desired developer use case (listening for clicks on the marker) with good ergonomics (`addEventListener` on the pseudo-element object). Crucially, it achieves this while adhering to the non-negotiable web compatibility requirement that `event.target` for standard events like `click` must remain the originating `Element`. While it requires implementing a modification to the event dispatch mechanism, this internal complexity seems justified to provide a usable and compatible solution. Option 1 is too cumbersome for developers, and Option 3 introduces a new event type which might be unnecessary if the dispatch modification in Option 2 can be generalized for other standard events targeting pseudo-elements.

Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/12163 using your GitHub account


-- 
Sent via github-notify-ml as configured in https://github.com/w3c/github-notify-ml-config

Received on Monday, 5 May 2025 13:40:42 UTC