【问题标题】:JS Custom Element get inner HTMLJS 自定义元素获取内部 HTML
【发布时间】:2019-08-08 11:15:33
【问题描述】:

我们有这样定义的自定义元素...

<my-button>
   Submit
</my-button>

和标准的customElements 定义

class MyButton extends HTMLElement{
   constructor(){
      super();
      // our custom code...
      this.innerHTML = ''; // ??? where did the 'Submit' go?
   }
}

...

customElements.define('my-button',MyButton);

问题是,在尝试获取 innerHTML 时,我们知道可以执行 DOMContentLoadedwindow.onload 之类的操作。

但有时我们希望使用代码动态创建“我的按钮”。并在附加时“渲染”...

有没有标准的方法来做到这一点?它与connectedcallback() 和其他“连接”功能有关吗?

谢谢!

请注意 - 我已尝试使用 connectedCallback() 作为可能的解决方案,但这并不能解决问题。

【问题讨论】:

标签: javascript html web-component shadow-dom


【解决方案1】:

在 Web 组件的构造函数中,您可以做什么和不可以做什么有一组规则。它们在下面。

但是想想这个:

可以通过以下三种方式之一创建组件:

  1. 初始页面的一部分/使用innerHTML:当浏览器加载页面或使用innerHTML 时,您可以将属性和子项添加到组件作为页面加载的一部分或innerHTML 的一部分。
parent.innerHTML = '<super-hero name="Thor"><super-weapon value="Mjolnir"></super-weapon></super-hero>'.
  1. 您可以调用document.createELement 来创建元素。在创建元素之前,您不能添加属性或子元素。
let superHero = document.createElement('super-hero');
let superWeapon = document.createElement('super-weapon');
superHero.setAttribute('name', 'Thor');
superWeapon.setAttribute('value',  'Mjolnir');
superHero.appendChild(superWeapon);
parent.appendChild(superHero)
  1. 您可以使用new 实例化一个对象。就像document.createElement 一样,您必须等到元素创建后才能添加属性和子元素。
let superHero = new SuperHero();
let superWeapon = new SuperWeapon();
superHero.setAttribute('name', 'Thor');
superWeapon.setAttribute('value',  'Mjolnir');
superHero.appendChild(superWeapon);
parent.appendChild(superHero)

一旦组件被创建,它就会被添加到 DOM 中,这时你的组件的 connectedCallback 就会被调用。

所有这三种方式都归结为使用new 进行实例化。 document.createElement 调用 CustomElementRegistry.get 以获取该元素的构造函数,然后使用 new 创建对象。

innerHTML 解析 HTML,然后调用 document.createElement 或使用 new 创建对象。

但是,这样做时,调用元素的构造函数时没有属性或子元素。实际上,调用connectedCallback 时可能没有任何子项或属性。这是在规范中添加observedAttributesattributeChangedCallback 的原因之一。

规范中缺少的一件事是知道有人在组件添加到 DOM 之前或之后添加或更改了子项。但是,如果你真的想知道孩子什么时候发生变化,你可以使用MutationObserver

这就是构造函数中不存在子元素或属性的原因。它们还没有被添加。

Now on to the rules:

在编写自定义元素构造函数时,作者受以下一致性要求的约束:

  • 对 super() 的无参数调用必须是构造函数主体中的第一条语句,以便在运行任何进一步的代码之前建立正确的原型链和此值。

  • return 语句不得出现在构造函数主体内的任何位置,除非它是简单的提前返回(return 或 return this)。

  • 构造函数不得使用 document.write() 或 document.open() 方法。

  • 不得检查元素的属性和子元素,因为在非升级的情况下不会出现任何内容,并且依赖升级会降低元素的可用性。

  • 元素不得获得任何属性或子元素,因为这违背了使用 createElement 或 createElementNS 方法的消费者的期望。

  • 一般来说,工作应尽可能推迟到 connectedCallback ——尤其是涉及获取资源或渲染的工作。但是请注意,connectedCallback 可以多次调用,因此任何真正一次性的初始化工作都需要一个守卫来防止它运行两次。

  • 一般来说,构造函数应该用于设置初始状态和默认值,并设置事件侦听器和可能的影子根。

在元素创建过程中会直接或间接检查其中一些要求,如果不遵循这些要求,将导致解析器或 DOM API 无法实例化自定义元素。

【讨论】:

  • 以前的规则链接坏了;新链接:html.spec.whatwg.org/multipage/…(而且帖子似乎有一个完整的“建议编辑”队列,所以我无法更新它)
【解决方案2】:

有两种方法。

  1. 您可以在window.onload 之后定义自定义元素,一切正常。
window.addEventListener('load', () => {
    customElement.define('my-button', MyButton);
});
  1. &lt;script&gt; 中设置defer 属性
<script defer src="my-button.js"></script>

【讨论】:

  • 在 99% 的情况下,您无需等待 load。您几乎总是可以在DOMContentLoaded 上更早地做到这一点。
【解决方案3】:

如果您需要可靠地访问自定义元素的子元素,无论您的自定义元素脚本如何放置在文档中,这都适合您:https://github.com/WebReflection/html-parsed-element

当解析器解析了自定义元素的子元素时,很难检测到; HTMLParsedElement 通过反复检查是否存在 nextSibling 来做到这一点(这意味着对当前元素的解析已完成)。当前的实现用一个突变观察器替换了这个检查,它检查自定义元素的父元素中的childListsubtree 变化。

【讨论】:

    猜你喜欢
    • 2021-11-26
    • 1970-01-01
    • 2020-11-07
    • 1970-01-01
    • 2021-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多