Search code examples
javascriptjqueryshadow-dom

Removing an enclosing div class with jQuery when matching within open shadow-root


I am using jQuery 3.7.1 to search for multiple span occurances with a class of "modified" and when found to have it remove the enclosing that has a class of "item".

This used to work before shadow Root, and would close all instances found in the HTML....

$("span.modified").closest("div.item").remove();

but doesn't anymore. I tried ....

$("span.modified").shadowRoot.closest("div.item").remove();

but no luck there either. I have sample code below where I am looking for the span inside the open shadow root and trying to remove the enclosing div which is not in shadow root. I have only recently learned about shadow root and I'm mystified. Any pointers in the right direction would be greatly appreciated. Additionally I am trying to replicate the original functionality of finding all spans on a page that may contain "modified" and closing all enclosing divs with a class of "item"

<div class="item">
       <a href="<SOME_LINK_HERE>" target="_blank">
            <x-cover volume="Test" id="23310203">
                #shadow-root (open)
                <div class="show" id="div">
                    <span class="modified">Modified</span>
                </div>
            </x-cover>
       </a>
</div>

Update: Just learned that jQuery is incapable of penetrating a Shadow DOM so the solution seems to be something along the lines of ....

  1. Use jQuery to find all x-cover elements.
  2. Iterate over these elements and use vanilla JavaScript to access their shadow DOMs.
  3. Inside the shadow DOM, find span elements with the class modified.
  4. For each span found, traverse up to the div.item and remove it.

I'll update with a solution if I find one of course.


Solution

  • Using DSD - Declarative ShadowDOM shadowrootmode="open" here to create one <x-cover>

    <div class="item">
      <a href="<SOME_LINK_HERE>" target="_blank">
        <x-cover volume="Test" id="23310203">
          <template shadowrootmode="open">
            #shadow-root (open)
            <div class="show" id="div">
              <span class="modified">Modified</span>
            </div>
          </template>
        </x-cover>
      </a>
    </div>
    
    <script>
      // delete class="item" having class="modified" in shadowRoot
      let x = document.body.querySelector("x-cover");
      console.log(x);
      let modified = x.shadowRoot.querySelector("span.modified");
      console.log(modified);
      let host = modified.getRootNode().host; // same as x here
      console.log(host);
      let div = host.closest("div.item");
      console.log(div);
    
      // div.remove();
    
    </script>

    If you need to process multiple elements with shadowRoot, you have to dive into the DOM structure and handle each individually

    const shadowDive = (
              el, 
              selector, 
              match = (m, r) => console.warn('match', m, r)
      ) => {
        let root = el.shadowRoot || el;
        root.querySelector(selector) && match(root.querySelector(selector), root);
        [...root.children].map(el => shadowDive(el, selector, match));
      }