16

I have a custom-element with shadow DOM, which listens to attribute target change.
target is supposed to be the ID of the element which my component is supposed to be attached to.

I've tried using querySelector and getElementById to get the element of the outer DOM, but it always returns null.

console.log(document.getElementById(target));
console.log(document.querySelector('#' + target));

Both of the above return null.

Is there a way to get a reference to the element in the parent document from within shadow DOM?

Penny Liu
  • 15,447
  • 5
  • 79
  • 98
Yuriy Kravets
  • 1,183
  • 2
  • 13
  • 28
  • 3
    show the full event code ? – prasanth Mar 11 '19 at 12:37
  • Ok, the problem was different, my component was inside another web-component with shadowDOM, that's why neither `querySelector` nor `getElementById` worked. – Yuriy Kravets Mar 11 '19 at 12:50
  • Is the element referenced by `target` in the DOM or in someone else's shadowDOM? `document.getElementBuId` does not traverse into shadowDOM and, thus, can not find an element within shadowDOM. If that element is in _your_ shadowDOM then you would use `this.shadowRoot.getElementById(target)`. Can you provide more example in your question so we can see where things are located? – Intervalia Mar 11 '19 at 15:57
  • @Intervalia both of those shadow DOMs are 'mine'. Since the element I was looking for was in shadow dom as well (doesn't matter which one) it wasn't found. – Yuriy Kravets Mar 11 '19 at 17:11
  • 1
    If you want to find a **parentNode** of a Custom Element outside (above) its ShadowDOM (like .``closest( )`` does in document DOM) you can traverse up the DOM tree (crossing shadowDOM boundaries) with this code: https://stackoverflow.com/questions/54520554/custom-element-getrootnode-closest-function-crossing-multiple-parent-shadowd – Danny '365CSI' Engelman Mar 14 '19 at 16:13
  • @Danny'365CSI'Engelman oh wow, that's really helpful, thanks ! – Yuriy Kravets Mar 14 '19 at 21:12

2 Answers2

16

You just have to call Shadow​Root.

this.shadowRoot.getElementById('target') should work.

Here's an example, the get syntax will bind an object property to a function.

get target() {
    return this.shadowRoot.getElementById('target');
}
Penny Liu
  • 15,447
  • 5
  • 79
  • 98
  • 3
    This answer is incorrect. The question was how to grab an element in `Document` from within the `Shadow DOM` and not the other way around. – Yuriy Kravets Jul 30 '22 at 16:36
  • A unduly complex answer is to recursively search the dom for elements with shadowRoot , and if an element has a shadowRoot, use its shadowRoot.getElementById.. A small fragment with this is shown below: document.getElementById('element-has-shadow-root').shadowRoot.getElementById('target'\) – giwyni Aug 16 '23 at 20:21
1

There are two use cases of shadow DOM as far as I can see:

  1. You control the the shadow DOM solely through your hosting custom element (like in the answer of @Penny Liu). If you want make sure no other script should call and alter the nodes than this is your choice. Pretty sure some banking websites use this method. You give up on flexibility though.

  2. You just want to scope some parts of your code for styling reasons but you like to control it via document.getElementById than you can use <slot>. After all, many libraries rely on the document object and will not work in shadow DOM.

Back to the problem, what you probably did was something like this:

shadowRoot.innerHTML = `...<script>document.getElementById('target')</script>`
// or shadowRoot.appendChild

This is NOT working! And this is not how shadow DOM was anticipated to work either.

Recalling method 2, you SHOULD fill your shadow DOM solely by <slot> tags. Most minimal example:

<!-- Custom Element -->
<scoped-playground>
  <style>some scoped styling</style>
  <div id="target"></div>
  <script>const ☝☝☝☝ = document.getElementById('target')</script>
</scoped-playground>

<!-- Scoped playground has a shadowRoot with a default <slot> -->
...
this.shadowRoot.innerHTML = "<slot>Everything is rendered here</slot>";
...

More advanced <slot> examples can be found at:
https://developers.google.com/web/fundamentals/web-components/shadowdom#composition_slot

ja0nz
  • 1,891
  • 1
  • 9
  • 5