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

The CSS Working Group just discussed ``How to handle addEventListener on `CSSPseudoElement`?``.

<details><summary>The full IRC log of that discussion</summary>
&lt;sakhapov> sakhapov<br>
&lt;masonf> sakhapov: pseudo elements and events. We always run into the CSS/HTML boundary.<br>
&lt;masonf> ... latest proposal is not to mess up HTML, and add pseudo target property to event object. And keep firing only on originating elements (real elements).<br>
&lt;masonf> ... pseudoTarget would just let you know where the event actually came from.<br>
&lt;masonf> ... just add information we need to existing stuff. And some potential syntactic sugar to add listener to the pseudo, which is equiv to a listener that checks .pseudoElement.<br>
&lt;masonf> s/pseudoElement/pseudoTarget<br>
&lt;flackr> q+<br>
&lt;masonf> sakhapov: special mention for boundary events: by default they should be turned off, like today. Going between pseudo and originating element, you just get one event.<br>
&lt;masonf> ... some option to be added to get multiple boundary events.<br>
&lt;lwarlow> q+<br>
&lt;astearns> ack flackr<br>
&lt;masonf> flackr: I suggest that we start simple, with just the pseudoTarget.<br>
&lt;masonf> ... opting into boundary events changes the set of events we have to fire, which has implications.<br>
&lt;masonf> ... just adding the pseudoTarget would handle a ton of use cases already<br>
&lt;masonf> ... we can separately do something more complicated later.<br>
&lt;keithamus> q+<br>
&lt;smaug> q+<br>
&lt;masonf> sakhapov: boundary events - the proposal is indeed not to change them now.<br>
&lt;masonf> flackr: we might eventually make that work by generating boundary events at *all* pseudo elements, rather than an opt-in. But for now, let's just do pseudoTarget.<br>
&lt;masonf> astearns: anne wants to know what pseudoTarget returns?<br>
&lt;masonf> sakhapov: CSSPseudoElement already exists, and that's what you get back. element.pseudo() function, and it returns this CSSPseudoElement. Persistent proxy to the pseudo element.<br>
&lt;masonf> annevk: does it return null when there's no pseudo element target.<br>
&lt;masonf> sakhapov: event.pseudoTarget will be null in that case<br>
&lt;astearns> ack lwarlow<br>
&lt;masonf> lwarlow: two questions. How does this work with nested pseudos?<br>
&lt;masonf> ... if I click on a ::before within ::details-contents, I'm guessing I get ::before?<br>
&lt;masonf> ... any way to know the nesting?<br>
&lt;masonf> sakhapov: right, target would be ::before. The CSSPseudoElement has a .parent relationship.<br>
&lt;masonf> lwarlow: other question is whether there's a limitation on which pseudos are supported?<br>
&lt;masonf> sakhapov: yes, currently only supports before after, scroll markers and buttons, ... and element-backed pseudo elements.<br>
&lt;masonf> lwarlow: details-content included?<br>
&lt;masonf> sakhapov: yes probably. Start small and don't break.<br>
&lt;masonf> lwarlow: don't expose ::-webkit-* stuff<br>
&lt;masonf> astearns: are we tying together what the pseudo element class implements and the .pseudoTarget property?<br>
&lt;lwarlow> q+<br>
&lt;masonf> ... whatever is exposed as a CSSPSeudoElement object can be put in this new property?<br>
&lt;masonf> sakhapov: so far yes, because it's currently limited and looking pretty safe. But we need to discuss.<br>
&lt;masonf> astearns: I'm concerned that the sets will diverge.<br>
&lt;lwarlow> We should update that CSS spec to remove the EventTarget parent type.<br>
&lt;lwarlow> q-<br>
&lt;masonf> keithamus: does this work with ::part()?<br>
&lt;astearns> ack keithamus<br>
&lt;masonf> ... or is that not exposed? That's element backed.<br>
&lt;masonf> sakhapov: but ::part() is a real element.<br>
&lt;masonf> ... just before/after/etc. Static list. Already spec'd.<br>
&lt;masonf> keithamus: does .parent get you the parent element?<br>
&lt;masonf> sakhapov: either originating element or parent pseudo element.<br>
&lt;masonf> keithamus: if you haven't clicked a pseudo, but you've clicked the element, you get .pseudoTarget==null, right?<br>
&lt;masonf> sakhapov: yes.<br>
&lt;masonf> keithamus: if you want ::details-content::before?<br>
&lt;masonf> sakhapov: details isn't supported yet<br>
&lt;masonf> keithamus: what about ::foo? Do I need to do extra work if I'm getting null because not supported or because wrong target.<br>
&lt;masonf> sakhapov: if selector is incorrect (per CSS rules) return null. Or not in the supported list, return null.<br>
&lt;annevk> q+ to ask if EventTarget inheritance is going to be removed<br>
&lt;masonf> keithamus: if the particular browser doesn't support a particular pseudo, I can't tell why I got null.<br>
&lt;lwarlow> `::details-content` really should be supported here unless there's a good reason not to.<br>
&lt;masonf> ... how do I feature detect this<br>
&lt;masonf> can't you use supports()?<br>
&lt;masonf> keithamus: I'm concerned about detecting feature support.<br>
&lt;masonf> sakhapov: just use supports()?<br>
&lt;masonf> keithamus: ok<br>
&lt;astearns> ack smaug<br>
&lt;masonf> smaug: I wonder about the click target. Click events aren't user events, they're based on down/up events. You may get the down on a pseudo, and up on another pseudo. Common ancestor is the click target. What's the .pseudoTarget in that case?<br>
&lt;masonf> sakhapov: if pseudo target on mousedown/up is the same, fire click with pseudo target. If different, pseudoTarget will be null.<br>
&lt;masonf> flackr: same common ancestor determination. Even if different, but there's a common pseudo element ancestor, you'd get that pseudo back.<br>
&lt;masonf> sakhapov: click will still be fired.<br>
&lt;keithamus> q+<br>
&lt;dbaron> I would expect it to work the same as "click" works for similar cases that are all elements.<br>
&lt;masonf> smaug: yep, just wondering about pseudoTarget in that case. Maybe that works. If you release the mouse on a pseudo element, maybe you expect the click there.<br>
&lt;masonf> flackr: that's not how we fire clicks.<br>
&lt;masonf> smaug: but this is new.<br>
&lt;masonf> flackr: we have an algorithm that finds the common ancestor.<br>
&lt;masonf> dbaron: I'd expect the existing algorithm to run on the tree that includes the pseudo elements.<br>
&lt;masonf> annevk: then we don't support selection?<br>
&lt;masonf> flackr: selection isn't tree abiding, so I wouldn't think we'd support it.<br>
&lt;astearns> ack annevk<br>
&lt;Zakim> annevk, you wanted to ask if EventTarget inheritance is going to be removed<br>
&lt;masonf> annevk: draft still has pseudo element inheriting from event target<br>
&lt;masonf> astearns: yes, draft is old<br>
&lt;dbaron> s/that includes the pseudo elements/as though it was all elements, and then we do whatever mapping we're deciding on here to whatever we would normally make the target/<br>
&lt;masonf> sakhapov: what do we think about the syntactic sugar part?<br>
&lt;masonf> annevk: that sounded pretty awful, I liked the suggestion to start smaller.<br>
&lt;astearns> ack keithamus<br>
&lt;masonf> keithamus: I'd agree. I'm wondering if we can have this as a string<br>
&lt;masonf> the .parent property, right?<br>
&lt;masonf> keithamus: seriously look at ::backdrop as part of this.<br>
&lt;masonf> sakhapov: ok I'll take a look<br>
&lt;flackr> getBoundingClientRect, computed style, other future extensions<br>
&lt;masonf> lwarlow: only details-content is element backed. Scroll marker is, but after/before aren't element backed.<br>
&lt;masonf> annevk: is the pseudo() API enshrined already? Seems strange to parse the argument with the CSS parser. Why not an enum?<br>
&lt;masonf> sakhapov: there can be arguments, functional pseudos, etc.<br>
&lt;masonf> annevk: ahh ok. Currently type returns one of three strings, I assume that's what can be returned?<br>
&lt;lwarlow> q+<br>
&lt;masonf> sakhapov: there's a separate discussion about how to return arguments.<br>
&lt;astearns> ack lwarlow<br>
&lt;masonf> lwarlow: is the pseudo function meant to be static?<br>
&lt;masonf> sakhapov: not static, exists on element. It's element.pseudo().<br>
&lt;masonf> lwarlow: there's CSS.pseudo(). For nested?<br>
&lt;masonf> sakhapov: yes<br>
&lt;masonf> lwarlow: I understand what keith said, might be nice to return string<br>
&lt;masonf> sakhapov: some pseudos exist more than once on a single element<br>
&lt;masonf> lwarlow: how would you know that here?<br>
&lt;masonf> sakhapov: you could add an index on CSSPseudoElement, or extend it, etc.<br>
&lt;masonf> astearns: I like having an object here, vs. a micro-syntax.<br>
&lt;masonf> keithamus: I agree. Was just asking what it gives you, but seems like it does have stuff.<br>
&lt;masonf> ... I don't think we should do indexing, that's what functional pseudos are for.<br>
&lt;lwarlow> q+<br>
&lt;masonf> sakhapov: how would it work with event handler, that just gets pseudo target back? How would you know the index?<br>
&lt;masonf> keithamus: you'd have hit testing. You'd have ::colon(5).<br>
&lt;masonf> sakhapov: no, as a developer<br>
&lt;masonf> keithamus: ahh how do I get it ahead of time to compare.<br>
&lt;masonf> sakhapov: you're handling click event, and you just get back a CSSPseudoElement. How do you tell which one was clicked?<br>
&lt;masonf> keithamus: depends on how the arguments are serialized.<br>
&lt;keithamus> `::column(5)`<br>
&lt;flackr> or ::view-transition-group(name)<br>
&lt;masonf> s/::colon/::column<br>
&lt;masonf> (sorry)<br>
&lt;masonf> keithamus: if there was a way to get the full string back, I could parse it and find the 5<br>
&lt;flackr> I like the idea of having args in some form<br>
&lt;masonf> ... would people be grabbing these objects ahead of time, or just inspect the object I get back<br>
&lt;masonf> ... always referential equality, or just ask the pseudo element about itself?<br>
&lt;masonf> ... worth exploring and asking developers which way looks easier<br>
&lt;flackr> qq+<br>
&lt;masonf> ... my hunch is that you'd just want to ask the CSSPseudoElement about itself, rather than enumerating them and comparing.<br>
&lt;astearns> ack flackr<br>
&lt;Zakim> flackr, you wanted to react to lwarlow<br>
&lt;masonf> flackr: commonly you'd just interrogate the interface. But longer term idea with the object is that it might have other useful properties like getBoundingClientRect() or parent()<br>
&lt;masonf> flackr: if you just get a string, you have to do a bunch more work.<br>
&lt;masonf> keithamus: I don't like the string either. Just the particulars of the design. Both are valid. Independently a developer might want to get a CSSPseudoElement and getBoundingClientRect.<br>
&lt;masonf> ... question is whether developer is likely to pre-fetch the pseudo element object.<br>
&lt;masonf> ... otherwise, it could just be a new element each time, rather than same object.<br>
&lt;masonf> flackr: I think it should be same object. Not hard for browser to do.<br>
&lt;masonf> flackr: similar to animation object - same throughout the lifetime of animation.<br>
&lt;masonf> keithamus: lifetime of the animation run. Pseudo elements last longer.<br>
&lt;masonf> keithamus: just pointing out that it likely doesn't matter much to developers.<br>
&lt;masonf> flackr: probably right, so it's a nice to have, if not hard.<br>
&lt;astearns> ack lwarlow<br>
&lt;masonf> lwarlow: if you take the ::column() example, and you know you're on index 2, how useful is that really? They're dynamic.<br>
&lt;masonf> ... so column 2 doesn't really mean much<br>
&lt;masonf> ... no meaning attached to column 2, e.g. which items are in that column.<br>
&lt;masonf> astearns: if you're doing click styling on a column, then you want to know which one to style.<br>
&lt;masonf> ... column pseudos aren't a thing yet either.<br>
&lt;masonf> sakhapov: you could have a scroll marker, one per page, and for statistics, you want to know which one people spend time on.<br>
&lt;masonf> lwarlow: intersection observer?<br>
&lt;masonf> sakhapov: better without IO<br>
&lt;masonf> flackr: analytics people like to know how far down in an article you got. Similar here, how many pages you saw<br>
&lt;masonf> keithamus: what events would you listen to for that?<br>
&lt;masonf> sakhapov: {missed}<br>
&lt;masonf> flackr: click on the scroll marker is a good indication<br>
&lt;masonf> lwarlow: could also use arrow keys<br>
&lt;masonf> flackr: not sufficient.<br>
&lt;masonf> lwarlow: IO gives you all of that for free.<br>
&lt;masonf> keithamus: analytics people like more data always<br>
&lt;masonf> ... there are novel use cases to have the index, and not hard to add, let's just add it<br>
&lt;masonf> lwarlow: index isn't stable. If the layout changes, indices change.<br>
&lt;masonf> keithamus: stable at the time. If you want to get "what are the current photos to be displayed" because at the time of click, things are stable.<br>
&lt;masonf> astearns: close it off here, times up<br>
&lt;lwarlow> More promising than before but still not fully sold personally<br>
&lt;masonf> astearns: sound like a promising avenue, but not ready to resolve. Maybe talk about in a full CSSWG meeting.<br>
&lt;masonf> sakhapov: I feel like we kind of agreed...?<br>
&lt;masonf> astearns: sounds like where we're going, but not quite there yet.<br>
&lt;masonf> astearns: discuss on the issue and talk at CSSWG.<br>
</details>


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


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

Received on Thursday, 2 October 2025 16:03:31 UTC