1

How can I access the content of the unnamed slot(='I need to access this text node' in this example) inside the web component's connectedCallback with javascript?

With javascript shadowRoot only shows it only as without any content inside. However Chrome renders it correctly as expected.

Thanks

Please note that the actual class constructor is omitted in the question for brevity.

 <template id="my-option-template">
         <slot></slot>
  </template>

.......

Using the template above

<my-option>I need to access this text node</my-option> 

2 Answers2

1

You have one or two tech behaviours to be aware of:

  • Slotted content is reflected from lightDOM to shadowDOM <slot>
    It is NOT moved.
    So you style lightDOM content in container CSS (global CSS in example 'green' color below)
    For long answer see: ::slotted CSS selector for nested children in shadowDOM slot

  • connectedCallback triggers on the opening tag <my-element>
    The rest of its (innerHTML) content was not parsed yet.
    Libraries help out with more callbacks that fire later..
    but why load 2 kB, and add dependencies,
    when you can write a tiny delay yourself using setTimeout (works as long as your lightDOM content is not huge)

  • Also see this answer: Contents of nested slots in web component are not visible

<template id="MY-ELEMENT">
   <h3>Slotted content:</h3>
   <slot></slot>
   <h3>Appended content:</h3>
   <h2></h2>
</template>

<style>
   my-element    { color:red }
   my-element h2 { color:green }
</style>

<my-element>
   <h2>need to access this text node</h2>
</my-element>

<script>
   customElements.define('my-element', class extends HTMLElement {
     constructor() {
       let template = (id) => document.getElementById(id).content.cloneNode(true);
       super() // sets and returns this scope
         .attachShadow({ mode: "open" }) // sets and returns this.shadowRoot
         .append(template(this.nodeName));
     }
     connectedCallback() {
       setTimeout(() => {
         this.shadowRoot.querySelector("H2").append(this.innerHTML)
       })
     }
   });
 </script>
Danny '365CSI' Engelman
  • 16,526
  • 2
  • 32
  • 49
1

It is possible to use this.shadowRoot.querySelectorAll('slot') in connectedCallback which would give you an NodeList for all defined slots. Content of slots can be accessed using the assignedNodes method. You then have the option to apply slotchange event listeners to the slot elements to be notified when slot content changes:

<my-option><b>I need to access this text node</b></my-option>


<script>
customElements.define('my-option', class extends HTMLElement {

    constructor() {

        super(); // sets and returns this scope

        var shadow = this.attachShadow({mode: 'open'});

        shadow.innerHTML ='<slot></slot>';
    }


    connectedCallback() {
        let slots = this.shadowRoot.querySelectorAll('slot');
        console.log(slots);

        slots[0].addEventListener('slotchange', function(e) {
            let nodes = slots[0].assignedNodes();
            console.log(nodes);
        });
    }
});
</script>

More info on https://developer.mozilla.org/en-US/docs/Web/API/HTMLSlotElement