Search code examples
javascripthtmlnative-web-component

How to access the data inside a SLOT in web-components (aka: custom components) with javascript


Im trying to understand how I can use <slot> properly. I made this example which I would like to be able to access the qty from the tag <li name="qty">102</li>, which has been exposed as a named slot in the web-component as <slot name="qty"></slot>

Running this example:

const template = document.createElement('template');
    template.innerHTML = `
    <style>
    slot{
        color: blue;
    }
    </style>
    
    <p>
        <slot></slot>
        <ul>
            <slot name="component"></slot>
            <slot name="qty"></slot>
        </ul>
    </p>
    `
    class SlotTest extends HTMLElement {
        constructor(){
            super();
            let shadow = this.attachShadow({mode: "open"});
            this.shadowRoot.appendChild( template.content.cloneNode(true) );
        }
        connectedCallback(){
            this.shadowRoot.querySelectorAll("slot").forEach(element => {
                if (element.name === '') {
                    console.info("unamed slot", element)
                } else{
                    console.info(`slot.innerText = "${element.innerHTML}"`, element)
                }
    
            });
        }
    }
    
    window.customElements.define('track-comp', SlotTest);
<track-comp>
<h2>TLC555CP</h2>
<li name="component">Timer IC</li>
<li name="qty">55</li>
</track-comp>
    
<track-comp>
<h2>2N2369</h2>
<li name="component">High Speed Switching Transistor</li>
<li name="qty">102</li>
</track-comp>

The innerText or HTML of the slot does not seem to be accessible. Is there a way to see this or do you have to add another property describing the quantity? And as for the un-nmed slot; how is that accessed inside the web component?

enter image description here

EDIT

I accept the answer but just wanted to share the correct thing would be to add an event listener like this in the constructor and handle the update that way.

this.shadowRoot.addEventListener('slotchange', event => console.log('event', event.target, this.nodeName, this.innerText));

So something like this would allow me to react to the slot update and view the slot content.

this.shadowRoot.addEventListener('slotchange', event => {
                console.info(this.nodeName);
                console.info(this.innerHTML);
            });

please take a look at this example for further details on slots


Solution

  • Slotted content is reflected to shadowDOM, NOT moved to shadowDOM

    For very very long answer see: ::slotted CSS selector for nested children in shadowDOM slot

    That means your slotted content remains in lightDOM

    And it is just a regular DOM operation:

    get quantity(){
        return ~~this.querySelector(`[name="qty"]`)?.innerHTML;
    }
    

    But be careful; if you component is defined before the Web Component lightDOM is parsed; as shown in the SO snippet below,
    you can not access lightDOM immediately, you have to wait for the innerHTML to be parsed (aka the Event Loop is empty) with setTimeout or requestAnimationFrame. BaseClasses like Lit, Stencil, LWC etc. will do this for you under de hood and provided you with an adoptedCallback (or something similar named)

    <script>
    customElements.define('track-comp', class extends HTMLElement {
      constructor(){
        super()
        .attachShadow({mode: "open"})
        .innerHTML = `<style>slot{color: blue}</style>
                      <p>
                        <slot></slot>
                        <ul>
                          <slot name="component"></slot>
                          <slot name="qty"></slot>
                        </ul>
                      </p>`;
      }
      get quantity(){
        return ~~this.querySelector(`[name="qty"]`)?.innerHTML;
      }
      connectedCallback(){
        console.log(this.nodeName,this.quantity);
        setTimeout(()=>{
          console.log(this.nodeName,this.quantity);
        });
      }
    });
    </script>
    
    <track-comp>
      <h2>TLC555CP</h2>
      <li name="component">Timer IC</li>
      <li name="qty">55</li>
    </track-comp>
    <track-comp>
      <h2>2N2369</h2>
      <li name="component">High Speed Switching Transistor</li>
      <li name="qty">102</li>
    </track-comp>