Proposal: SelectorObserver #1285
Labels
addition/proposal
New features or enhancements
needs implementer interest
Moving the issue forward requires implementers to express interest
What problem are you trying to solve?
The TL;DR is: there are many good reasons to observe when a selector matches
N
number of elements, and whenN
changes. But here are some more concrete examples:Example 1: Observing state only exposed in CSS.
Often times an elements state can only be properly observed via CSS selectors, for e.g.
:dir()
&:lang()
can match implicit parents (ref whatwg/html#7039 /cc @claviska),:is(:popover-open, :modal, :fullscreen)
are the only way to observe if something is in the top layer (ref whatwg/html#8783 /cc @straker, ref whatwg/html#9075 /cc @sanajaved7), and with CSS CustomStates a custom element can hide any number of state transitions behind the:state()
selector without exposing equivalent observable properties from JS, such as events.Counter: It could be argued that state that is only observable in CSS should remain there (although
.matches()
seems to belie that). It could also be argued that any state exposed in CSS should have an equivalent state exposed in JS.Example 2: Observing when a new element is added that matches a given selector.
It can be useful to simply discover when new elements come in or out of the DOM that match a given selector; for example many implementations of a "custom element lazy define" (ref WICG/webcomponents#782 /cc @justinfagnani) seek this approach. See also a more generic proposal for
MountObserver
(ref WICG/webcomponents#896 /cc @bahrus).Counter: Finding elements matching a selector is quite possible with
MutationObserver
but the code gets unwieldy very quickly, and it can quickly run into issues where the main thread is blocked because it's re-running querySelector on a large DOM, on each mutation.Example 3: Ergonomic API for attaching a behaviour to new elements as they enter/leave the page
For a very long time, most of GitHub's JS was powered by the principle that we could observe elements as the enter the DOM. The https://github.com/josh/selector-observer library powered this, and it allowed developers to express a selector, which would fire a callback whenever the element count for the selector changed. This library concatenates all the given selectors into one giant querySelector (which, fun story, caused some regressions in Firefox from stylo, ref https://bugzilla.mozilla.org/show_bug.cgi?id=1422522).
What solutions exist today?
MutationObserver
& the selector-observer library are probably the closest.How would you solve it?
I'd propose making a new
SelectorObserver
class which has similar ergonomics to other*Observer
classes. You pass it a callback, and a selector, and it can lazily accumulate records and give them to the callback. EachSelectorObserverRecord
could represent one observed selector, andaddedNodes
could represent newly matching nodes (either because they've just entered the DOM, or because they've now changed state), whileremovedNodes
could represent no-longer-matching nodes (either because they've changed state, or just left the DOM):The IDL might look a little like:
For the
:dir()
/:lang()
example in whatwg/html#7039 (@claviska) it'd look a bit like:The top-layer example in whatwg/html#8783 & whatwg/html#9075 (@sanajaved7, @straker) it'd look a bit like:
For the Custom Elements examples in WICG/webcomponents#782 (@justinfagnani) & WICG/webcomponents#896 (@bahrus) might become:
Anything else?
Hopefully folks don't mind the pings, but I thought it would be nice to encourage some discussion from those who have similar use-cases.
The text was updated successfully, but these errors were encountered: