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

Ok, trying to incorporate all the feedback I've got during the last discussion in this new proposal.

When a user interacts with a pseudo-element, the browser will fire a single event on the ultimate originating (real) element, just like it does today. This guarantees old websites are unaffected.

To provide the new information, let's add to the event object a new property - `event.pseudoTarget`: A reference to the specific `CSSPseudoElement` that was hit, or null if the interaction was on the real element. So, `event.target` remains the real element to ensure backward compatibility.

On top of this, let's add two "syntactic sugar" shortcuts for developers:

### Attaching a listener directly to the CSSPseudoElement object:
```
const scrollMarker = list.pseudo('::scroll-marker');
scrollMarker.addEventListener('click', handler);
```
which would be translated by the browser into:
```
list.addEventListener('click', (event) => {
  if (event.pseudoTarget === scrollMarker) {
    // If the check passes, it calls the handler.
    handler(event);
  }
});
```

### Top-level filtering:
```
// Tell the browser to only notify about '::scroll-marker' clicks.
body.addEventListener('click', myAnalyticsHandler, { pseudoTarget: '::scroll-marker' });
```
which would be translated by the browser into:
```
body.addEventListener('click', (event) => {
  // It adds a check against the `CSSPseudoElement.type`.
  if (event.pseudoTarget?.type === '::scroll-marker') {
    // If the name matches, it calls the handler.
    myAnalyticsHandler(event);
  }
});
```

This proposal should cover most use cases, seems to be safe for web compat and is quite future proof.
We would also start only with pseudos which can currently be represented by [`CSSPseudoElement`](https://drafts.csswg.org/css-pseudo/#CSSPseudoElement-interface):
::after, ::before, ::marker + newly added ::scroll-marker and ::scroll-button(*).
Also, we can start with just a small explicit number of events?

A few notes covering other suggestions:

* Mouse boundary events would be off by default - the browser will keep firing only one mouseover when a mouse is moving over a pseudo or its ultimate originating element - as it does now. If there will be a request for it we can add some opt-in during addEventListener/some global opt-in?

* @keithamus Specific new events for e.g. `::scroll-marker` clicks:
  Sounds good, but probably won't be needed given this proposal above? Even with specific event types we need some way to understand which specific pseudo has been clicked (would be covered by `event.pseudoTarget`).

* @annevk's comment:
  * The source would be the ultimate originating element. We will lose the ability to say which specific pseudo has been the source in this case, but that's probably ok to do so?
  * AT and focus traversal: note, that we defined [focus traversal](https://drafts.csswg.org/css-overflow-5/#focus-order) and AT roles for `::scroll-marker` and `::scroll-button` pseudos and likely can do so for other pseudos, the idea for now is to start with a small number of supported pseudos. But the concern is very valid, given that ultimate originating element can be not interactive, but its pseudo can be interactive. This would require some additional spec work for AT and focus order.



-- 
GitHub Notification of comment by danielsakhapov
Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/12163#issuecomment-3174668663 using your GitHub account


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

Received on Monday, 11 August 2025 12:55:08 UTC