My <web-component></webcomponent>
is very simply built, a series of <input type="text part="input" />
directly in the #shadow-root
.
I can style them all at one without any troubles with:
web-component::part(input) {
border: 1px solid #000
}
But neither of these works if I want to target a specific input:
web-component::part(input):nth-child(3) {}
web-component::part(input):nth-of-type(3) {}
web-component::part(input).input-3 {} /* with class properly defined in the web component */
web-component::part(input.input-3) {}
web-component::part(input:nth-of-type(3)) {}
...
Can’t find any documentation about this.
EDIT : I forgot to mention that the inputs are not slotted, they’re generated dynamically.
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.classList.add('input-'+(i+1));
input.type = "text";
this.shadowRoot.appendChild(input);
}
}
}
);
web-component::part(input) {
border: 2px solid orange;
}
web-component::part(input):nth-child(1) {
background: #ff00ff;
}
web-component::part(input):nth-of-type(2) {
background: #ff0000;
}
web-component::part(input).input-3 {
background: #00ff00;
}
web-component::part(input.input-4) {
background: #00ffff;
}
web-component::part(input:nth-of-type(5)) {
background: #ff00ff;
}
web-component::part(input:nth-chlid(6)) {
background: #ff0000;
}
<web-component size="6"></web-component>
2
Answers
It can’t be done this way. From W3C css-shadow-parts spec:
All selectors you are trying are either not pseudo-classes or pseudo-classes that based on tree information.
Fortunately, part names act similarly to classes: multiple elements can have the same part name, and a single element can have multiple part names. (From the same spec as well.)
Hence, you can use multiple part names such as
input input-1
.Like 9 out of 10 developers your are falling into the DOM parsed trap,
they experience no issues because they all define their Web Components (too) late.
There is no DOM when the Web Component is defined before DOM is parsed:
connectedCallback
Only in the connectedCallback are the attributes available.
Note! There is no
.innerHTML
lightDOM yet!Because the connectedCallback fires on the opening tag
See my Dev.to blogpost
But its save to attach shadowDOM here. And create DOM with a handy helper function
createElement
And you do not want to use
appendChild
inside a loop, because it causes unnecessary reflow/paints for every item.Be aware
The
connectedCallback
is called again when you move the Web Component in the DOM (append, drag-drop etc.)