'WebComponents: how to get the resolved value of a slot in shadow DOM?

I have a web component and I want to adjust the value of its slot.

Unfortunately, I am unable to get the resolved value from it.

How can I do that?

const template = document.createElement('template');
template.innerHTML = `
    <p><slot></slot></p>
`;

class MyComp extends HTMLElement {
    constructor() {
        super();
        this.root = this.attachShadow({mode: 'open'});
        this.root.appendChild(template.content.cloneNode(true));
    }

    connectedCallback() {
        const slot = this.shadowRoot.querySelector('slot');
        console.log('VALUE:', slot.innerText);  // always empty
    }
}

customElements.define('my-comp', MyComp);
<my-comp>abc</my-comp>


Solution 1:[1]

You can assign an EventListener to watch for SLOT changes

MDN Documentation

customElements.define('my-element', class extends HTMLElement {
    constructor() {
      super();
      this.attachShadow({
        mode: 'open'
      }).appendChild(document.getElementById(this.nodeName).content.cloneNode(true));
    }

    connectedCallback() {
      this.listeners = [...this.shadowRoot.querySelectorAll("SLOT")].map(slot => {
        let name = "slotchange";
        let func = (evt) => {
          let nodes = slot.assignedNodes();
          console.log(`Slot ${slot.name} changed to ${nodes[0].outerHTML}`)
        }
        slot.addEventListener(name, func);
        return () => slot.removeEventListener(name, func); // return cleanup function!!!
      })
    }
    disconnectedCallback() {
      this.listeners.forEach(removeFunc => removeFunc());
    }
  });
<template id="MY-ELEMENT">
  <style>
    ::slotted(*) {
      background: yellow;
      margin: 0;
    }

    ::slotted(span) {
      color: red;
    }

  </style>
  <b>
    <slot name=title></slot>
  </b>
  <slot name=content></slot>
</template>

<my-element>
  <span slot="title">Hello World</span>
  <p slot="content">What a wonderful day!</p>
</my-element>
<my-element>
  <span slot="title">Hello Tomorrow</span>
  <p slot="content">What will you bring?</p>
</my-element>

JSFiddle: https://jsfiddle.net/CustomElementsExamples/vu9w1zyx/

Solution 2:[2]

Less code, easier to understand. For simple templates I'd rather create the elements programmatically, then add a slotchange listener:

class MyComp extends HTMLElement {
    p = document.createElement('p');
    slot = document.createElement('slot');
    
    constructor() {
        super();
        this.p.append(this.slot);
        this.attachShadow({mode: 'open'}).append(this.p);
        this.slot.addEventListener('slotchange', () => console.log('VALUE:', this.slottedValue))
    }

    get slottedValue() { return this.slot.assignedNodes()[0].wholeText }
}

customElements.define('my-comp', MyComp);

console.log(document.querySelector('my-comp').slottedValue);
<my-comp>abc</my-comp>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 connexo