7

仅当 DOM 准备好并且我们可以确定所有元素都可用时才开始操作它是古老的常识,在后 jQuery 时代,我们都DOMContentLoaded为此使用事件。

现在 Web 组件(尤其是自治自定义元素的形式)倾向于创建自己的 HTML,通常采用connectedCallback()生命周期方法。

第一个问题:

DOMContentLoaded(自主)自定义元素有何关系?只有在所有组件 connectedCallbacks 完成后才会发生事件吗?如果没有,我如何确保某些代码仅在 Web 组件完成初始化后执行?

第二个问题,完全相关:

Web 组件如何与元素的defer属性相关联script

4

2 回答 2

1

I'm not into web-components, but I would say... not at all.

Your component is being defined by your script, but before it is, the browser will still parse the markup and execute all the synchronous scripts as usual, and fire the DOMContentLoaded when it's done.

So if you do define your CustomElements synchronously before the DOMContentLoaded event fired, then your elements connectedCallback will have fired (because it's not an Event, it's a callback, and is called synchronously).

if (window.customElements) {

  addEventListener('DOMContentLoaded', e => console.log('DOM loaded'));

  class MyCustom extends HTMLElement {
    connectedCallback() {
      console.log('Custom element added to page.');
    }
  }

  customElements.define('my-custom', MyCustom);
  console.log('Just defined my custom element')

} else {
  console.log("your browser doesn't have native support");
}
<my-custom></my-custom>

But if you do wait for the DOMContentLoaded event, then... the callbacks will fire after.

if (window.customElements) {

  addEventListener('DOMContentLoaded', e => console.log('DOM loaded'));

  class MyCustom extends HTMLElement {
    connectedCallback() {
      console.log('Custom element added to page.');
    }
  }

  setTimeout(()=> customElements.define('my-custom', MyCustom), 2000);

} else {
  console.log("your browser doesn't have native support");
}
<my-custom></my-custom>

But in no way is DOMContentLoaded waiting for anything else than the end of the synchronous execution of all the scripts, just like if you didn't had any CustomElement in there.


As to your last question, as said in the docs about the defer attribute, the scripts with such an attribute will get parsed before the DOMContentLoaded fires.

于 2018-07-16T13:24:54.390 回答
1

They are on different axes. DOMContentLoaded is about parsing the initial HTML document, so the raw "file" which is downloaded. Components are ready when they are defined.
I am not familiar with the topic either, so just modified the MDN example to completely decouple the two happenings via a button press. I assumed definition of new components can happen any time, and indeed that is the case.
MDN example is on GitHub, linked from CustomElementRegistry.define (and also from various other pages). They have a live variant too on GitHub IO, but of course that is just the original example.
Putting the complete example here feels hopeless, so this is just the modified HTML:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Pop-up info box — web components</title>
  </head>
  <body id="body">
    <h1>Pop-up info widget - web components</h1>

    <form>
      <div>
        <label for="cvc">Enter your CVC <popup-info img="img/alt.png" text="Your card validation code (CVC) is an extra security feature — it is the last 3 or 4 numbers on the back of your card."></label>
        <input type="text" id="cvc">
      </div>
    </form>

    <!--<script src="main.js"></script>-->
    <script>
      function test(){
        var x=document.createElement("script");
        x.src="main.js";
        document.getElementById("body").appendChild(x);
      }
      customElements.whenDefined("popup-info").then(function(){alert("popup-info");});
      document.addEventListener("DOMContentLoaded",function(){alert("DOMContentLoaded")});
    </script>
    <button onclick="test()">Test</button>
  </body>
</html>

So main.js is not loaded automatically, only after pressing the button, and it still works. "DOMContentLoaded" popup has happened by a long time then. However, there is a CustomElementRegistry.whenDefined() which is patient, and fires only after the custom element gets defined. I think this is what you could use, perhaps subscribing to it from DOMContentLoaded, so your final event would be guaranteed to happen when both the DOM and the custom element is ready. Downside is that you have to know the name of the custom element(s) you are waiting for. (Untested assumption, but based on the scheduling diagrams on https://html.spec.whatwg.org/multipage/scripting.html, one could probably make whenDefined() to happen before DOMContentLoaded. By the way: the diagrams also show what defer does, so if you put whenDefined() into a deferred script, the callback will happen after DOMContentLoaded, or at least that is what I believe)

于 2018-07-16T14:48:01.060 回答