Search code examples
slotsstenciljs

How to determine number of children in a slot


Is there anyway to know how many children a named slot contains? In my Stencil component I have something like this in my render function:

<div class="content">
  <slot name="content"></slot>
</div>

What I want to do is style the div.content differently depending on how many children are inside the slot. If there are no children in the slot, then div.content's style.display='none', otherwise, I have a bunch of styles applied to div.content that make the children appear correctly on the screen.

I tried doing:

  const divEl = root.querySelector( 'div.content' );
  if( divEl instanceof HTMLElement ) {
    const slotEl = divEl.firstElementChild;
    const hasChildren = slotEl && slotEl.childElementCount > 0;
    if( !hasChildren ) {
      divEl.style.display = 'none';
    }
  }

however this is always reporting hasChildren = false even when I have items inserted into the slot.


Solution

  • If you are querying the host element you will get all the slotted content inside of it. That means that the host element's children are going to be all the content that will be injected into the slot. For example try to use the following code to see it in action:

    import {Component, Element, State} from '@stencil/core';
    
    @Component({
      tag: 'my-component',
      styleUrl: 'my-component.css',
      shadow: true
    })
    export class MyComponent {
      @Element() host: HTMLElement;
      @State() childrenData: any = {};
    
      componentDidLoad() {
        let slotted = this.host.children;
        this.childrenData = { hasChildren: slotted && slotted.length > 0, numberOfChildren: slotted && slotted.length };
      }
    
      render() {
        return (
        <div class="content">
          <slot name="content"></slot>
          <div>
            Slot has children: {this.childrenData.hasChildren ? 'true' : 'false'}
          </div>
          <div>
            Number of children: {this.childrenData.numberOfChildren}
          </div>
        </div>);
      }
    }