What problem are you trying to solve?
It is currently exceedingly difficult (and slow) to reliably react to the existence of certain element types or element + attribute combinations across shadow roots.
This is necessary for use cases like:
- Implementing HTML polyfills for new element types or new attributes
- Augmenting built-in elements in a certain way (e.g. to implement certain behavior for a
data- attribute)
- Lazy loading of web components
The only existing tool is MutationObserver, however these use cases require observing mutations in shadow trees as well, which is currently incredibly difficult. All existing solutions have serious flaws: prohibitive performance, enforcing tightly coupled architectures, obtrusiveness, and/or miss too many cases.
What solutions exist today?
1. 🤷🏽♀️ Explicit opt-in
Use a regular document-wide mutation observer and simply accept that the polyfill will not work correctly when shadow trees are involved unless explicitly invoked on them by expecting components that use a shadow root to invoke a certain method, e.g. discover() (example) on their shadow root once they create it.
This only works in tightly coupled architectures. A third-party component cannot possibly know what such functions to invoke.
2. 🙈 Monkey patch
Wrap HTMLElement.prototype.attachShadow() with code that calls mutationObserver.observe() on it, like so:
let originalAttachShadow = HTMLElement.prototype.attachShadow;
HTMLElement.prototype.attachShadow = function(...args) {
let node = originalAttachShadow.call(this, ...args);
mutationObserver.observe(node, options);
return node;
}
This is the direction this polyfill has gone.
This fails for:
- declarative shadow DOM
- pre-existing shadow roots
3. 🤕 Brute force
When the mutation observer detects any new elements, check if they have a shadowRoot and call observe() on that too. Loop over all existing elements and attach mutation observers to the shadow roots of those with one.
This is extremely wasteful, and still does not catch every case, notably failing for elements that do not have a shadow root when inspected, but get one later.
How would you solve it?
Option 1: New MutationObserverInit option
Add a new option to MutationObserverInit to observe shadow trees. Exact name & syntax TBB, but it needs to support:
- Observing across all open trees
- Observing across certain closed roots (passing them by reference)
- Both of the above
Later expansions might be:
- Distinguish open vs open-stylable
- Max level of depth
Option 2: :has-shadow + observe shadow attachment
There is already a CSS WG issue for :has-shadow: w3c/csswg-drafts#11007
Then, we can add a shadowAttached (again, TBB) option to MutationObserverInit to observe shadow root attachment. That can just be a boolean option, since we can already detect whether a closed shadow root exists, so we don't need the additional granularity, passing root references etc.
If we can detect which elements have a shadow root + which elements get a shadow root, we have all the pieces of the puzzle that we need to put this together.
What problem are you trying to solve?
It is currently exceedingly difficult (and slow) to reliably react to the existence of certain element types or element + attribute combinations across shadow roots.
This is necessary for use cases like:
data-attribute)The only existing tool is
MutationObserver, however these use cases require observing mutations in shadow trees as well, which is currently incredibly difficult. All existing solutions have serious flaws: prohibitive performance, enforcing tightly coupled architectures, obtrusiveness, and/or miss too many cases.What solutions exist today?
1. 🤷🏽♀️ Explicit opt-in
Use a regular document-wide mutation observer and simply accept that the polyfill will not work correctly when shadow trees are involved unless explicitly invoked on them by expecting components that use a shadow root to invoke a certain method, e.g.
discover()(example) on their shadow root once they create it.This only works in tightly coupled architectures. A third-party component cannot possibly know what such functions to invoke.
2. 🙈 Monkey patch
Wrap
HTMLElement.prototype.attachShadow()with code that callsmutationObserver.observe()on it, like so:This is the direction this polyfill has gone.
This fails for:
3. 🤕 Brute force
When the mutation observer detects any new elements, check if they have a
shadowRootand callobserve()on that too. Loop over all existing elements and attach mutation observers to the shadow roots of those with one.This is extremely wasteful, and still does not catch every case, notably failing for elements that do not have a shadow root when inspected, but get one later.
How would you solve it?
Option 1: New
MutationObserverInitoptionAdd a new option to
MutationObserverInitto observe shadow trees. Exact name & syntax TBB, but it needs to support:Later expansions might be:
Option 2:
:has-shadow+ observe shadow attachmentThere is already a CSS WG issue for
:has-shadow: w3c/csswg-drafts#11007Then, we can add a
shadowAttached(again, TBB) option toMutationObserverInitto observe shadow root attachment. That can just be a boolean option, since we can already detect whether a closed shadow root exists, so we don't need the additional granularity, passing root references etc.If we can detect which elements have a shadow root + which elements get a shadow root, we have all the pieces of the puzzle that we need to put this together.