2

I have a custom element, my-el, which in its DOM template (a shadow root) contains a slot element. In a web page, I use my-el by placing additional elements in between the tags <my-el> </my-el> as shown below.

<my-el>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
</my-el>

I intend to measure the divs' dimensions placed in my-el as they are in the distributed tree, as soon as (i.e. the first time) they are rendered on the page. According to the spec:

To assign slotables, for a slot slot with an optional suppress signaling flag (unset unless stated otherwise), run these steps:

  1. Let slotables be the result of finding slotables for slot.

  2. If suppress signaling flag is unset, and slotables and slot’s assigned nodes are not identical, then run signal a slot change for slot.

...

If I have understood correctly, the first distribution of slot slottables does not fire a slotchange event since the suppress signaling flag is set when the slot has not performed an initial distribution, further stated in Shadow DOM v1: Self-Contained Web Components. This means that I cannot make an initial measurement of the children elements in the callback of slotchange. Further, the following approach does not guarantee that at that point the children will be rendered:

connectedCallback() {
  super.connectedCallback();

  requestAnimationFrame(() => {
    // Measure chilren
  });
}

Question

How can I trigger my measurements upon the first time they are rendered after being distributed in the slot in my-el?

WoodyWoodsta
  • 160
  • 2
  • 15

1 Answers1

1

You can define a variable in your custom element that will be set the first time your elements are rendered (either in connectedCallback or at slotchange event).

Then check the variable value to decide whether it's a first time rendering.

customElements.define( 'c-e', class extends HTMLElement 
{
    connectedCallback () 
    {
        var sh = this.attachShadow( { mode: 'open' } )
        sh.appendChild( T1.content.cloneNode( true ) )
        
        sh.querySelector( 'button').onclick = ev =>   
            this.appendChild( document.createElement( 'div') ).innerHTML = 'new div'

        sh.querySelector( 'slot' ).addEventListener( 'slotchange', ev => 
            !len && ( len = this.render() ) )

        var len = this.render()
    }

    render () 
    {
        var count = this.querySelectorAll( 'div' ).length
        if ( count )
            console.info( '%s elem. first rendered in %s', count, this.id )
        return count
    }
} )
<c-e id=CE1>
  <div>div 1</div>
  <div>div 2</div>
</c-e>
<c-e id=CE2>
</c-e>

<template id=T1>
  <style>
    :host {
      display: inline-flex;
      flex-direction: column;
      width: 150px;
      border: 1px solid lightblue;
      min-height: 4em;
      margin: 5px;
      background: #f7f7f7;
    }
  </style>
  <div>
    <button>add div</button>
  </div>
  <slot>
  </slot>
</template>
Supersharp
  • 29,002
  • 9
  • 92
  • 134
  • 1
    This doesn't _quite_ answer the question, but is close. I'm looking for the point at which I can **guarantee** that the elements will be rendered in the DOM. In your example, making measurements in `connectedCallback` works, but is it because this is a simple example? In other words, how can we guarantee rendering? (note that "guarantee rendering" does not imply "force rendering") – WoodyWoodsta Jan 19 '17 at 05:45
  • 1
    Rendering is synchronous so I don't see the point. Maybe you should give a concrete example where the rendering is not guaranteed in connectedCallback. Differrent use cases have already been discussed (http://stackoverflow.com/q/40378115, http://stackoverflow.com/q/36187227, http://stackoverflow.com/q/35805252, http://stackoverflow.com/q/33602247) – Supersharp Jan 19 '17 at 08:07
  • I have had a good look at the questions in the links. I also put together a more comprehensive fiddle to experiment. It appears that it is indeed synchronous, so as long as you measure after the point of injecting the template into the DOM, measuring should be warranted? I'm using [Polymer](https://www.polymer-project.org/1.0/) to design my components and apps, and their element rendering lifecycle is more complicated. I was hoping that there was a platform technique, and not an effect brought about by the framework architecture. So this one if up to the framework API! Thanks for the help! – WoodyWoodsta Jan 19 '17 at 18:58