html - 将影子 DOM 附加到自定义元素可以消除错误,但为什么呢?

标签 html shadow-dom custom-element

根据 custom element specification ,

The element must not gain any attributes or children, as this violates the expectations of consumers who use the createElement or createElementNS methods.

在这种情况下,Firefox 和 Chrome 都会正确抛出错误。但是,附加影子 DOM 时,不会出现任何错误(在任一浏览器中)。

火狐:

NotSupportedError: Operation is not supported

Chrome :

Uncaught DOMException: Failed to construct 'CustomElement': The result must not have children

没有影子DOM

function createElement(tag, ...children) {
  let root;

  if (typeof tag === 'symbol') {
    root = document.createDocumentFragment();
  } else {
    root = document.createElement(tag);
  }

  children.forEach(node => root.appendChild(node));

  return root;
}

customElements.define(
  'x-foo',
  class extends HTMLElement {
    constructor() {
      super();

      this.appendChild(
        createElement(
          Symbol(),
          createElement('div'),
        ),
      );
    }
  },
);

createElement('x-foo');

使用 shadow DOM

function createElement(tag, ...children) {
  let root;

  if (typeof tag === 'symbol') {
    root = document.createDocumentFragment();
  } else {
    root = document.createElement(tag);
  }

  children.forEach(node => root.appendChild(node));

  return root;
}

customElements.define(
  'x-foo',
  class extends HTMLElement {
    constructor() {
      super();

      // it doesn't matter if this is open or closed
      this.attachShadow({ mode: 'closed' }).appendChild(
        createElement(
          Symbol(),
          createElement('div'),
        ),
      );
    }
  },
);

createElement('x-foo');

请注意:为了查看示例,you need to be using (至少)以下之一:Firefox 63、Chrome 67、Safari 10.1。不支持边缘。

我的问题如下:

根据规范,行为是否正确?

将子节点添加到根节点会导致 DOM 重排;如果没有影子 DOM,如何避免这种情况?

最佳答案

每次创建元素都是通过构造函数完成的。但是,当调用构造函数时,没有子项也没有任何属性,这些都是在创建组件之后添加的。

即使该元素是在 HTML 页面中定义的,它仍然是由代码使用构造函数创建的,然后由解析 HTML 页面中的 DOM 的代码添加属性和子元素。

调用构造函数时,没有子元素,您不能添加它们,因为 DOM 解析器可能会在构造函数完成后立即添加它们。相同的规则适用于属性。

目前没有办法指定 shadowDOM 或 shadowDOM child ,除非通过 JS 代码。 DOM 解析器不会向 shadowDOM 添加任何子元素。

因此根据规范,在构造函数中访问、更改属性或子项或对其进行任何操作都是非法的。但是,由于 DOM 解析器无法向组件 shadowDOM 添加任何非法内容。

在不使用 shadowDOM 时,我已经解决了这个问题,方法是使用在构造函数中创建的内部模板元素,然后在调用 connectedCallback 时将其作为子元素放置。

// Class for `<test-el>`
class TestEl extends HTMLElement {
  constructor() {
    super();
    console.log('constructor');
    const template = document.createElement('template');
    template.innerHTML = '<div class="name"></div>';
    this.root = template.content;
    this.rendered = false;
  }

  static get observedAttributes() {
    return ['name'];
  }

  attributeChangedCallback(attrName, oldVal, newVal) {
    if (oldVal !== newVal) {
      console.log('attributeChangedCallback', newVal);
      this.root.querySelector('.name').textContent = newVal;
    }
  }

  connectedCallback() {
    console.log('connectedCallback');
    if (!this.rendered) {
      this.rendered = true;
      this.appendChild(this.root);
      this.root = this;
    }
  }

  // `name` property
  get name() {
    return this.getAttribute('name');
  }
  set name(value) {
    console.log('set name', value);
    if (value == null) { // Check for null or undefined
      this.removeAttribute('name');
    }
    else {
      this.setAttribute('name', value)
    }
  }
}

// Define our web component
customElements.define('test-el', TestEl);

const moreEl = document.getElementById('more');
const testEl = document.getElementById('test');
setTimeout(() => {
testEl.name = "Mummy";
  const el = document.createElement('test-el');
  el.name = "Frank N Stein";
  moreEl.appendChild(el);
}, 1000);
<test-el id="test" name="Dracula"></test-el>
<hr/>
<div id="more"></div>

此代码在构造函数中创建一个模板并使用 this.root 来引用它。 调用 connectedCallback 后,我将模板插入 DOM 并将 this.root 更改为指向 this,这样我对元素的所有引用仍然存在工作。

这是一种让您的组件始终能够保持其子项正确的快速方法,而无需使用 shadowDOM 并且仅在调用 connectedCalback 后才将模板作为子项放入 DOM 中。

关于html - 将影子 DOM 附加到自定义元素可以消除错误,但为什么呢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53180428/

相关文章:

javascript - base href 获取不正确,不知道从哪里获取

javascript - polymer 在运行时更改自定义样式变量

javascript - 自定义元素 : monkeypatch disconnected callback

javascript - 自定义 HTMLElement 的 connectedCallback() 中的 textContent 为空

javascript - CSS 背景大小 : cover and background positioning for a 1000x100 graphic

html - 导航栏下的空白区域,只有在下一节的标题中添加边框时才会消失

html - 有没有办法在 IE11 中查看 Shadow DOM?

javascript - LitElement connectedCallback() 与 firstUpdate()

javascript - html td问题: hide a td value if another has a value 'not available'

CSS:如何在 Shadow DOM 根目录中定位::有槽的 sibling ?