在 connectedCallback 中等待元素升级:FireFox 和 Chromium 的区别

wait for Element Upgrade in connectedCallback: FireFox and Chromium differences

2021 年 3 月更新:

修复了 FireFox 错误,现在与 Chromium 和 Safari 的行为相同。

这意味着在connectedCallback中等待JS EventLoop为空(用setTimeoutrequestAnimationFrame)现在是一个跨浏览器的方法

connectedCallback(){
 setTimeout(()=>{
   // can access lightDOM here
 }); // ,0 not required
}

事件循环到底是什么? - 菲利普·罗伯茨
https://www.youtube.com/watch?v=8aGhZQkoFbQ



2020 年 10 月 28 日更新:



第一个post五月。 2020 年:

在使用 FireFox 一周后,再次被这个 Chrome 元素升级问题困扰。

在交付给 Chromium 浏览器之前忘记将代码包装在 setTimeout 中。

问题:为什么不一样?

<script>
  customElements.define('my-element', class extends HTMLElement {
    connectedCallback() {
      console.log(this.innerHTML);// "A" in FireFox, "" in other Browsers
      if (this.innerHTML == "A")
        this.innerHTML = this.innerHTML + "B";
      else
        setTimeout(() => this.innerHTML = this.innerHTML + "D");
    }
  })
</script>

<my-element>A</my-element><my-element>C</my-element>

历年相关回答:

更新 #1

注意:Chromium Blink 引擎是 Apples (WebKit)WebCore 代码的一个分支!!

更新 #2

通过 Supersharps 参考,我们找到了相关主题:

FireFox 与 Chromium 中的回调顺序:

来源:https://jsfiddle.net/CustomElementsExamples/n20bwckt/

我认为 Chrome/Safari 行为对于初学者来说不太直观,但对于一些更复杂的场景(例如子自定义元素),它会更加一致。

请参阅下面的不同示例。他们在 Firefox 中的行为很奇怪...

另一个我没有勇气编码的用例:当解析文档时,可能你还没有结束文档。因此,创建自定义元素时,在获得结束标记(永远不会到达)之前,您无法确定是否获得了所有子元素。

根据 Ryosuke Niwa 对于 WebKit 的说法:

The problem then is that the element won't get connectedCallback until all children are parsed. For example, if the entire document was a single custom element, that custom element would never receive connectedCallback until the entire document is fetched & parsed even though the element is really in the document. That would be bad.

所以最好不要等到创建后立即连接自定义元素,这意味着没有子元素。

<script>
    customElements.define( 'c-e', class extends HTMLElement {} ) 
    customElements.define('my-element', class extends HTMLElement {
      connectedCallback() {
        console.log(this.innerHTML, this.childNodes.length)
        let span = document.createElement( 'span' )
        if (this.innerHTML.indexOf( 'A' ) >= 0 )
            span.textContent = 'B'
        else
            span.textContent = 'D'
        setTimeout( () => this.appendChild( span ) )
      }
    })
</script>
<my-element>A</my-element><my-element>C</my-element>
<br>
<my-element><c-e></c-e>A</my-element><my-element>A<c-e></c-e></my-element>
<br>
<my-element><c-e2></c-e2>A</my-element><my-element>A<c-e2></c-e2></my-element>

据我所知,就此达成共识,导致以 (Chrome/Safari) 方式调整规范:

Fixes w3c/webcomponents#551 by ensuring that insertions into the DOM trigger connectedCallback immediately, instead of putting the callback reaction on the the backup element queue and letting it get triggered at the next microtask checkpoint. This means connectedCallback will generally be invoked when the element has zero children, as expected, instead of a random number depending on when the next custom element is seen.

我们可以得出结论,Firefox 也遵循规范...是的,但由于上述原因,我们不应依赖 connectedCallback 中的内容。