shadow dom - know when dom is rendered/changed
Asked Answered
V

2

5

I am writing a chrome extension that modifies elements properties as a page is loaded or changes.

I do this using a Mutation Observer. However the observer's handler is not called when shadow-dom (ie, embedded twitter posts) are loaded/changed.

Is there a way to get an event when shadow-dom is loaded/changes or to hook a mutation observer to it ?

Thanks!

Vomit answered 28/10, 2017 at 23:15 Comment(0)
I
11

You can simply observe() the shadowRoot property of the element with a Shadow DOM.

customElements.define('image-list', class extends HTMLElement {
  connectedCallback() {
    var observer = new MutationObserver(function(mutations) {
      mutations.forEach(function(mutation) {
        //Detect <img> insertion
        if (mutation.addedNodes.length)
          console.info('Node added: ', mutation.addedNodes[0])
      })
    })

    this.attachShadow({mode: 'open'}).innerHTML = '<img alt="image 1">'

    observer.observe(this.shadowRoot, {childList: true})
  }

  addImage() {
    var img = document.createElement('img')
    img.alt = ' new image '
    this.shadowRoot.appendChild(img)
  }
})
<image-list id=LI>
</image-list>
<button onclick="LI.addImage()">Add image</button>
Indifference answered 29/10, 2017 at 15:49 Comment(5)
great thx ! does that mean I need my main observer to hook a new observer to any shadow dom it encounters?Vomit
@Vomit yes you willIndifference
This answer is helpful, but what if I need to detect the addition of the shadow root itself? The mutationObserver does not trigger when the initial shadow root is added to the <image-list> element. I tried updating the observer to watch the parent element instead, but it doesn't seem to be able to detect the addition of the shadowRoot.Electrojet
I haven't found an instance/use case yet where the shadowRoot is added outside of the custom element constructor. I would feel pretty confident that if I waited for customElements.whenDefined(nodeName) to resolve that if a shadowRoot wasn't there it wasn't going to be. I guess it depends on your use case and how sure you need to be.Nielson
@Keven I'm afraid it's not possible. Another workaround would be to overload the attachShadow() method. That's monkey patching and not always recommanded.Indifference
F
0

if the contents are simply getting slotted using the slotchange event is easiest, it doesn't fire on deeper changes, just the slotting (unlike a mutation observer watching a subtree and childList); it's like an observer of childList on the slot and the best way to separate concerns between a component and slotted content; when observing changes in a component's own respective shadowRoot or deeper in a tree for some reason, use the mutation observer in @Supersharp's answer except adding subtree to the config (or possibly setup children with their respective shadowRoots to dispatch an event with {composed: true, cancelable: true, bubbles: true} notifying ancestors up to your component of the change;

there'd need to be a slot added to the shadowRoot and the event listener added to it like shadowRoot.innerHTML = <slot onslotchange=...>empty slot</slot> adapted to work in-context

https://developer.mozilla.org/docs/Web/API/HTMLSlotElement/slotchange_event

Florindaflorine answered 30/10, 2021 at 11:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.