DOMContentLoaded与Web组件有何关系?

时间:2018-07-16 12:47:14

标签: javascript web-component deferred-loading domcontentloaded

只有在DOM准备就绪后才开始操作DOM,这是一种古老的常识,我们可以确定所有元素都可用,并且在jQuery后的日子里,我们都为此使用DOMContentLoaded事件

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

第一个问题:

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

第二个问题,完全相关:

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

2 个答案:

答案 0 :(得分:1)

我不喜欢Web组件,但我会说……一点也不。

您的组件是由脚本定义的,但是在此之前,浏览器仍会解析标记并照常执行所有同步脚本,并在完成后触发DOMContentLoaded。

因此,如果您确实在触发 DOMContentLoaded事件之前同步定义了CustomElements,则您的元素connectedCallback将被触发(因为它不是事件,而是回调,因此称为< em>同步)。

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>

但是如果您确实等待DOMContentLoaded事件,则...回调将在之后触发。

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>

但是,除了所有脚本的同步执行结束之外,DOMContentLoaded都不会等待任何其他事情,就像您在那里没有任何CustomElement一样。


关于您的最后一个问题,如关于defer属性的in the docs所述,具有该属性的脚本将在之前解析DOMContentLoaded。

答案 1 :(得分:1)

它们在不同的轴上。 DOMContentLoaded是关于解析初始HTML文档的,因此将下载原始的“文件”。定义组件后即可使用它们。
我也不熟悉该主题,因此只修改了MDN示例以通过按钮完全将这两种情况分离。我以为新组件的定义可以随时发生,确实如此。
MDN示例为on GitHub,它是从CustomElementRegistry.define(以及其他各个页面)链接而来的。他们也有一个实时变体on GitHub IO,但是当然那只是原始示例。
将完整的示例放在这里感觉毫无希望,所以这只是修改后的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>

因此,main.js不会自动加载,只有在按下按钮后才能正常加载。然后,“ DOMContentLoaded”弹出窗口发生了很长时间。但是,有一个CustomElementRegistry.whenDefined()是耐心的,只有在定义了自定义元素后才触发。我认为这是您可以使用的,也许是从DOMContentLoaded订阅的,所以可以确保在DOM和自定义元素都准备就绪时发生您的最终事件。缺点是您必须知道等待的自定义元素的名称。 (未经验证的假设,但根据https://html.spec.whatwg.org/multipage/scripting.html上的调度图,可能使whenDefined()发生在DOMContentLoaded之前。顺便说一句:这些图还显示了defer的作用,因此,如果您将whenDefined()放入延迟脚本中,则回调将在DOMContentLoaded之后发生,或者至少是我认为的那样