How can a web-component get its own styles (width etc) as soon as possible?

女生的网名这么多〃 提交于 2019-12-07 00:37:30
MarcG

I seem to have found the solution, using requestAnimationFrame:

proto.attachedCallback = function () {

    // This never works:
    console.log("(1) test-element.width = ", this.offsetWidth);

    // This works sometimes:
    window.setTimeout(function () { console.log("(2) test-element.width = ", this.offsetWidth); }.bind(this), 0);

    // SOLUTION - This seems to always work.
    window.requestAnimationFrame(function () { console.log("(4) test-element.width = ", this.offsetWidth); }.bind(this));
    };

Note: This is so far working OK for me, no matter how many web components I nest, and no matter how complicated I chain my HTML imports. However... does this always work? HTML parsing of HTML imports is parallelized (according to user https://stackoverflow.com/users/274673/ebidel here: http://www.html5rocks.com/en/tutorials/webcomponents/imports) and I don't perfectly understand the situation to be absolutely sure of this solution.

In your example above, the fastest way to get the (custom) element width in a <script> tag just after the element, exactly what you did in your test (3):

<test-element></test-element>

<script>
    var testElement = document.querySelector('test-element');
    console.log("(3) test-element.width = ", testElement.offsetWidth);
</script>

It's because HTML elements are rendered sequencially by the browser. So the script content will be executed just after the calculation of the <test-element> width and other layout properties.

(1) test-element.width =  0    
(3) test-element.width =  500
HTMLImportsLoaded  //with polyfill
DomContentLoaded   
window.onload
(2) test-element.width =  500
WebComponentsReady //with polyfill 

NB: Other browser than Chrome will behave differently.

Update

Be careful not getting the computed style values too soon in the rendering, especially if another element is using the requestAnimationFrame method, too.

Below an example that shows a web component cannot always rely on this hack to know itself: the grid element doesn't know its rendered width at step (4) yet.

(function(window, document) {
  var proto = Object.create(HTMLElement.prototype);

  proto.attachedCallback = function() {

    // Direct output.
    console.log("(1) %s.width = ", this.id, this.offsetWidth);
    this.innerHTML = "Element: " + this.id
    
    // Delayed output.
    window.setTimeout(function() {
      console.log("(2) %s.width = %s ", this.id, this.offsetWidth);
    }.bind(this), 0);

    window.requestAnimationFrame(function() {
      if (this.id == "side")
        this.classList.add("large")

      console.log("(4) %s.width = %s", this.id, this.offsetWidth);
    }.bind(this));
  };

  document.registerElement('test-element', {
    prototype: proto
  });

})(window, document);
test-element {
  display: inline-block;
  border: 1px solid red;
  width: 500px;
}

.large {
  width: 300px;
}
<!DOCTYPE html>
<html>

<head>

</head>

<body style="display:flex">
  <test-element id="grid"></test-element>
  <test-element id="side"></test-element>

  <script>
    var testElement = document.querySelector('test-element');
    console.log("(3) %s.width = %s", testElement.id, testElement.offsetWidth);

    window.onload = function() {
      console.log("window.onload")
    }
  </script>

</body>

</html>
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!