Notification texts go here Contact Us Buy Now!

How to target the Nth element in a webcomponent shadowDOM with CSS ::part()

Targeting the Nth Element in a Web Component's Shadow DOM with CSS ::part()

CSS ::part() pseudo-element allows you to style specific named DOM elements within a shadow DOM. While it's commonly understood that ::part() can be used to target specific elements, it's crucial to understand its limitations.

According to the css-shadow-parts spec, ::part() cannot be extended with pseudo-classes or match elements based on tree information. This means that selectors using structural pseudo-classes like :nth-child(), :first-child(), or :last-child() are not supported.

The following examples demonstrate why ::part() with structural pseudo-classes is not allowed:

/* Invalid: Targeting the 3rd input using :nth-child() */
web-component::part(input:nth-child(3)) {
  background-color: red;
}

/* Invalid: Targeting the first input using :first-child() */
web-component::part(input:first-child) {
  background-color: blue;
}

/* Invalid: Targeting the last input using :last-child() */
web-component::part(input:last-child) {
  background-color: green;
}

These selectors are not valid because they rely on the structural relationship between elements in the shadow DOM, which is not accessible via ::part().

Fortunately, there's a workaround that leverages the fact that part names, similar to CSS classes, can be applied to multiple elements. This allows you to achieve the same effect as using structural pseudo-classes by assigning multiple part names to the desired elements.

Here's an example that demonstrates how to target the Nth element using multiple part names:

customElements.define("web-component", 
        class extends HTMLElement {
            constructor() {
                super();
                this.attachShadow({mode: "open"});
                this.size = this.getAttribute('size');
                this.template = document.createElement("template");
                this.template.innerHTML = '<style>'
                +':host {white-space: nowrap}'
                +'input {text-align:center;width: 3ch;height: 3ch}'
                +'input:not(:last-child){margin-right:.5ch}'
                +'</style>'
                this.render();
            }

            render() {
            
                this.shadowRoot.appendChild(this.template.content.cloneNode(true));

                for (let i = 0; i < this.size; i++) {
                    const input = document.createElement('input');
                    input.setAttribute('part','input input-'+(i+1));
                    input.type = "text";
                    this.shadowRoot.appendChild(input);
                }
            }
        }
    );
web-component::part(input input-1)  {
  background: #ff00ff;
}

web-component::part(input) {
  border: 2px solid orange;
}
<web-component size="6" id="shadow-dom-host"></web-component>

In this example, each input element is assigned two part names: "input" and "input-N", where N is the 1-based index of the input. The CSS rules target the first input using ::part(input input-1) and style all inputs generally using ::part(input).

While this workaround allows you to achieve the desired styling, it's important to note that ::part() with structural pseudo-classes is not supported and may result in unexpected behavior in some cases.

Remember, the primary purpose of ::part() is to target specific named DOM elements within a shadow DOM, not for selecting elements based on their structural relationship.

Post a Comment

Cookie Consent
We serve cookies on this site to analyze traffic, remember your preferences, and optimize your experience.
Oops!
It seems there is something wrong with your internet connection. Please connect to the internet and start browsing again.
AdBlock Detected!
We have detected that you are using adblocking plugin in your browser.
The revenue we earn by the advertisements is used to manage this website, we request you to whitelist our website in your adblocking plugin.
Site is Blocked
Sorry! This site is not available in your country.