只有在DOM准备就绪后才开始操作DOM,这是一种古老的常识,我们可以确定所有元素都可用,并且在jQuery后的日子里,我们都为此使用DOMContentLoaded
事件
现在,Web组件(尤其是以自治自定义元素的形式)倾向于通常在connectedCallback()
生命周期方法中创建自己的HTML。
第一个问题:
DOMContentLoaded
与(自治)自定义元素有何关系?仅在所有组件connectedCallbacks完成之后才发生该事件吗?如果没有,我如何确保某些代码仅在Web组件完成初始化后才能执行?
第二个问题,完全相关:
Web组件如何与defer
元素的script
属性相关?
答案 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
之后发生,或者至少是我认为的那样