Search code examples
javascripthtmlweb-component

Web Component offsetHeight/offsetWidth zero when connected


I am trying to convert a library of mine into a web component and I have run into a problem with when to tell the component has been laid out.

Web Components have a callback called connectedCallback which looks like it might be what I need, but when inspecting self in the callback, my offset dimensions are both zero:

connectedCallback() {
  console.log(this.offsetWidth, this.offsetHeight);
}

When I queue up the dimensions query, the dimensions are there, so sometime between the web component connecting and the event queue reaching the next item, the web component gets laid out by the browser engine, gaining correct dimension values:

connectedCallback() {
  setTimeout(() => console.log(this.offsetWidth, this.offsetHeight), 0);
}

I am wondering if there is a callback to tell when the layout event has happened?


Solution

  • I created a simple component without shadowDOM. I wrote the following code in a constructor:

    this.addEventListener('readystatechange', (evt) => {console.log('readystatechange', evt)});
    this.addEventListener('load', (evt) => {console.log('load', evt)});
    this.addEventListener('progress', (evt) => {console.log('progress', evt)});
    this.addEventListener('DOMContentLoaded', (evt) => {console.log('DOMContentLoaded', evt)});
    

    And got no output. So none of these events trigger on a Web component.

    Then I added this code outside the component:

    function callback(mutationsList, observer) {
      for(var mutation of mutationsList) {
        if (mutation.type === 'childList') {
          if (mutation.removedNodes.length) {
            console.log(`${mutation.removedNodes.length} child node(s) have been removed.`);
          }
    
          if (mutation.addedNodes.length) {
            console.log(`${mutation.addedNodes.length} child node(s) have been added.`);
          }
        }
      }
    }
    var config = { childList: true };
    var observer = new MutationObserver(callback);
    

    And this line to the constructor:

    observer.observe(this, config);
    

    On my particular component my callback was never called.

    I did have a different component that changes its children and the callback was called.

    So, I don't see any event or even mutation observer that triggers when the component is fully loaded.

    Personally I would use a setTimeout like you did since that seems to be the only sure way of getting a call when the component is done. Although you may have to be specific where you put your setTimeout to make sure you have fully rendered your DOM.