在不同影子根下的一个 HTML 页面中有多个具有相同 ID 的(内部)元素在语义上是否正确?

Is it semantically correct to have multiple (inner) elements with the same ID in one HTML page under different shadow roots?

我正在使用 Stencil 构建 Web 组件,最近与一位同事就 shadowRoot 及其影响进行了一些讨论。假设我有以下带有 a 标签的简单组件,其中 idlink-button。如果有多个同类组件,父 HTML 页面会发生什么情况?在同一个 HTML 页面上有多个具有相同 ID 的组件在语义上是错误的,还是可以的,因为每个 shadowRoot 都是它自己的缩影,而包含的 HTML 页面不是知道这个所谓的,隐藏,内容?

@Component({
  tag: 'link-button',
  styleUrl: 'button.scss',
  shadow: true,
})
export class LinkButton {

  render() {
    return <a id='link-button'></a>;
  }
}

这是这个组件在检查时的样子。想象一下其中的 10 个。

不同的影子树可能包含具有相同 id 的元素,因为正如您所说,影子树本质上是“微观世界”。

使用 HTML 生活标准来证明这一点:

The id Attribute

When specified on HTML elements, the id attribute value must be unique amongst all the IDs in the element's tree and must contain at least one character. The value must not contain any ASCII whitespace.

要了解标准如何定义“树”,我们转向 DOM 标准。

Trees

An object that participates in a tree has a parent, which is either null or an object, and has children...

The root of an object is itself, if its parent is null, or else it is the root of its parent. The root of a tree is any object participating in that tree whose parent is null.

一个元素的“树”由它的所有 parents 组成,直到它到达一个“根”(和它们的 children),以及该元素的 children.

标准区分了document trees and shadow trees,因为它们有不同的词根:

A document tree is a node tree whose root is a document.

A shadow tree is a node tree whose root is a shadow root.

换句话说,shadow tree的root和document的root是不一样的,为了定义id,他们在不同的tree中。

但为了彻底,我们必须考虑文档的 children 是否包含其 children 的影子树。为此,我们转向 Node Tree.

的定义

直接引用比较难,但本质上树可以是包含元素的文档,也可以是文档片段。与此同时,元素被定义为包含其他元素或 CharacterData 节点,特别是 not DocumentFragments.

拼图的最后一块是 shadow roots are DocumentFragments,因此为了定义树,它们不被视为 children 文档。

或者总结一下:

  • ID 在树中必须是唯一的。
  • 一棵树是一个根和它的最后一个 children 之间的所有元素。
  • 文档树和影子树有不同的根。
  • 文档树不包含阴影树 children。
  • 因此,不同的 Shadow Trees 可能包含相同的 id。

  • .

很好的例子,其中“正常”浏览器行为不是书面标准所说的。

这可以追溯到 25 多年前,当时(重复的)ID 在 Internet Explorer
中有意义 所有后来的浏览器都复制了它。

过去 20 年的浏览器行为:

  • 全局 ID 创建同名的全局变量
    仅在全局范围内!不在 shadowRoots 中!
  • 重复的 ID 创建一个包含 HTMLCollection 的全局变量,
    但是 Mozilla 团队在几年前“修复”了这个问题,FireFox 现在的行为类似于 getElementById 和 returns DOM[=56 中的 first 元素=]
  • .querySelectorAll("#MYID") returns 具有多个元素的有效节点列表

这一切都是因为网络向后兼容,1996 年的网站今天应该 运行 没问题。

所以正确答案是:

是的,您可以有 重复的 ID 值,您是否取决于您的需要.

如果我看到初级人员在(他们自己的)实验代码中使用 getElementID,我会教育他们他们在浪费击键。
如果我在生产代码中看到这个浏览器 magic,我会解雇他们。
如果我看到他们在 shadowRoot 中使用这些知识,我会将他们提升为高级,因为他们了解该平台。

<div id="MYID"></div>
<div id="MYID"></div>
<my-el id=MYID></my-el>
<my-el id=MYID></my-el>

<script>
  customElements.define(`my-el`, class extends HTMLElement {
    connectedCallback() {
      this.attachShadow({
        mode: "open"
      }).innerHTML = (`<p id="MYID"></p>`).repeat(3);
      console.log("NodeList",this.shadowRoot.querySelectorAll("#MYID"));
    }
  });
  console.log("global variable MYID:",MYID); // global IDs create a global Variable
</script>