何时调用自定义元素构造函数?(HTMLTemplateElement.content问题)

Jor*_*uis 0 javascript constructor web-component custom-element native-web-component

看看这个简单的例子 (不要理会这个,去编辑)

class MyElement extends HTMLElement {

  customProperty = "something";

  constructor() {
    super();

    console.log("My Element Constructor");
  }
}

customElements.define("my-element", MyElement);

document.body.innerHTML = "<my-element></my-element>";
var myElement = document.querySelector("my-element");
console.log(myElement); // 
console.log(myElement.customProperty); //
Run Code Online (Sandbox Code Playgroud)

输出:

<my-element>
undefined
My Element Constructor
Run Code Online (Sandbox Code Playgroud)

在主调用堆栈完成之前,不会调用自定义元素构造函数(但它是 HTMLElement 构造函数)。这是预期行为还是错误?

谢谢!

编辑

为了简化我的实际情况,我提出了前面的示例,但我现在认为它不适合说明我的问题(该示例确实可以正常工作)。感谢@connexo的回答,我能够隔离问题(这是一个具有许多依赖项的复杂项目)并将其转换为以下示例:

<my-element>
undefined
My Element Constructor
Run Code Online (Sandbox Code Playgroud)

由于某种原因,当模板元素的内容被添加到shadowRoot时,这些元素将不会被解析,直到shadowRoot被添加到DOM。但是当直接修改shadowRoot的innerHTML时,即使shadowRoot不属于DOM,也会解析该内容。

感谢您的时间。

con*_*exo 5

问:何时调用自定义元素构造函数?

答:分三种情况:

  1. 如果您的元素在解析之前<my-element已在自定义元素注册表中注册,则在解析HTML 部分时会调用其构造函数。请注意缺少的内容>- 对于这种情况,这是解析器在调用构造函数时解析的所有内容 -没有属性,没有子元素)。

  2. 如果您的元素在解析之前尚未在自定义元素注册表中注册,则在自定义元素注册完成后将立即调用构造函数。这称为升级案例。升级之前,浏览器的自定义元素只是一个HTMLUnknownElement.

  3. new MyElement()当您使用或动态创建自定义元素时,也会调用构造函数document.createElement('my-element')。请注意,a) 在这两种情况下,属性和子元素都不存在,b) 使用调用构造函数的时间点创建元素时,document.createElement('my-element')将是 1. 或 2.(取决于自定义元素是否已注册) 。

只要您确保代码在document.body可用之前不会运行,您的代码就会按预期工作。

实现此目的的最简单方法是仅在紧邻结束标记之前包含脚本</body>

如果您的 JS 在外部文件中,一个简单的替代方法是使用 boolean 属性包含它defer

<script src="./path/to/my/script.js" defer></script>
Run Code Online (Sandbox Code Playgroud)

第二种选择是将代码包装在DOMContentLoaded侦听器中(这样,将标签放在哪里并不重要script,也不需要属性defer):

<script src="./path/to/my/script.js" defer></script>
Run Code Online (Sandbox Code Playgroud)

所有三种方法基本上都强制执行升级案例 (2.),在多年创作大型 Web 组件库的专业实践中,这已被证明是最可靠和最简单的方法。

编辑

由于您现在完全改变了问题,因此答案如下:

HTML<template>元素是一种保存 HTML 的机制,该 HTML 不会在页面加载时立即呈现,但可能随后在运行时使用 JavaScript 进行实例化

将模板视为存储以供文档中后续使用的内容片段。<template>虽然解析器在加载页面时确实处理元素的内容,但它这样做只是为了确保这些内容有效;但是,该元素的内容并未呈现。

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template

然而,它HTMLTemplateElement有一个 content 属性,它是一个只读的,DocumentFragment包含模板表示的 DOM 子树。请注意,直接使用内容的值可能会导致意外行为,请参阅下面的避免 DocumentFragment 陷阱部分。
https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template#attributes