0

I want to create <slot>s for each child, so for example here I have a menu and I want each child to be placed inside a <div> with item class.
I have created a little utility function to map the children:

export function ChildrenMap(el: LitElement, map: (el: HTMLElement, index: number) => TemplateResult): TemplateResult[] {
    console.log(el.children.length) // Returns 0 in Google Chrome and I guess Edge too.
    return (Array.prototype.slice.call(el.children) as HTMLElement[])
                .map(map);
}

And then I am using it in the render function:

render() {
    return html`
        <nav>
        ${ChildrenMap(this, (ele, index) => {
            let id = ele.id ? ele.id :  `item-${index}`;
            ele.setAttribute('slot', id);

            let klass = 'item';
            if(this.selected && this.selected == index) {
                klass += " selected";
            }

            return html`
                <div class="${klass}" data-index="${index}">
                <slot name="${id}"></slot>
                </div>`;
        })}
        </nav>
    `;
}

This works fine in FireFox, but as my comment above says, in Google Chrome, the element has 0 children at the point of render, so the <nav> is empty.
Can anybody explain why the element has 0 children at the point of render? Perhaps I am going about this the wrong way, does anybody have any alternatives to do this?

Many thanks

Luke T O'Brien
  • 2,565
  • 3
  • 26
  • 38

1 Answers1

1

Update: Mozilla/FireFox fixed this bug early 2021

Actually FireFox is wrong at this point, it invokes the connectedCallback too late

Bug confirmed by Mozilla engineer Anne van Kesteren:

For details see StackOverflow thread:

There are no DOM children to read yet in the connectedCallback because all DOM children are still being parsed. (it could potentially be a very large DOM structure)

The work-around (like you found) is to wait:

connectedCallback(){
  setTimeout(()=>{
    // now you can access this. DOM children
  },0);
}

All suggestions with Promises etc. effectively do the same: Wait till the Event Loop is empty

Danny '365CSI' Engelman
  • 16,526
  • 2
  • 32
  • 49