I'm using Stencil to build web components and recently had a little talk with a colleague about shadowRoot and implications. Suppose I have the following simple component with a a tag which has the id of link-button. What happens in the parent HTML page if there are several components of the same kind? Is it semantically wrong to have several components with the same ID on the same HTML page, or is it alright since each shadowRoot is a microcosm of its own and the containing HTML page is not aware of this so called, hidden, content?

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

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

This is how this component looks during inspection. Imagine 10 of these.

enter image description here

2

There are 2 best solutions below

0
On BEST ANSWER

Different shadow trees may contain elements with the same id since, as you said, shadow trees are essentially "microcosms".

Using the HTML Living Standard to prove this:

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.

To understand how the standard defines "tree", we turn to the DOM standard.

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.

The "tree" of an element consists of all its parents until it hits a "root" (and their children), and the element's children.

The standard makes a distinction between document trees and shadow trees because they have different roots:

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.

In other words, the root of a shadow tree is not the same as the root of the document, and hence they are in different trees for the sake of defining an id.

But to be thorough, we have to consider whether a document's children includes the shadow trees of its children. For that we turn to the definition of Node Tree.

It's harder to quote directly, but essentially a tree can either be a Document which contains Elements, or a DocumentFragment. Elements, meanwhile, are defined to contain either other Elements or CharacterData nodes, notably not DocumentFragments.

The final piece of the puzzle is that shadow roots are DocumentFragments, which are therefore not considered children of Documents for the sake of defining a tree.

Or to summarize:

  • An id must be unique within a tree.
  • A tree is all the elements between a root and its last children.
  • Document Trees and Shadow Trees have different roots.
  • Document Trees do not contain Shadow Trees as children.
  • Therefore, different Shadow Trees may contain the same id.

0
On

Great example where "normal" Browser behavior is not what the written standard says.

This goes back 25+ years, when (duplicate) IDs had a meaning in Internet Explorer
which all later browsers copied.

Browser behavior for the past 20 years:

  • global IDs create a Global Variable with the same name
    Only in Global Scope! Not in shadowRoots!
  • duplicate IDs create a Global Variable containing an HTMLCollection,
    but the Mozilla team "fixed" this some years ago, FireFox now behaves like getElementById and returns the first element in the DOM
  • .querySelectorAll("#MYID") returns a valid NodeList with multiple elements.

All because the Web is backwards compatible, a website from 1996 should run fine today.

So the correct answer is:

Yes, you can have duplicate ID values, should you depends on your needs.

If I see a junior use getElementID in (their own) experimental code, I educate them they are wasting keystrokes.
If I see this Browser magic in production code, I fire them.
If I see them using this knowledge in a shadowRoot, I promote them to senior, because they understand the platform.

<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>