Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

<!DOCTYPE html>
<meta charset="utf-8">
<title>attr() looks up on the originating element across shadow boundaries</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<style>
/* ::part() rule from outside the shadow tree: attr() reads from the host. */
#host-part::part(label) {
color: attr(data-color type(<color>), red);
font-size: attr(data-size type(<percentage>), 100%);
}
/* ::part() reaching into a deeper shadow via exportparts: attr() reads from the
outer host (the rule's match target). */
#host-outer::part(inner-label) {
color: attr(data-color type(<color>), red);
}
</style>
<part-host id="host-part" data-color="green" data-size="200%"></part-host>
<outer-host id="host-outer" data-color="green"></outer-host>
<div id="slotted-host">
<template shadowrootmode="open">
<style>
/* ::slotted() rule inside a shadow tree: attr() reads from the slotted element. */
::slotted(span) {
color: attr(data-color type(<color>), red);
}
</style>
<slot></slot>
</template>
<span data-color="green">slotted</span>
</div>
<script>
customElements.define("part-host", class extends HTMLElement {
connectedCallback() {
const root = this.attachShadow({ mode: "open" });
const span = document.createElement("span");
span.setAttribute("part", "label");
root.appendChild(span);
}
});
// outer-host wraps inner-host and exports inner-host's "label" part as "inner-label",
// so the document's #host-outer::part(inner-label) rule cascades two shadow boundaries deep.
customElements.define("inner-host", class extends HTMLElement {
connectedCallback() {
const root = this.attachShadow({ mode: "open" });
const span = document.createElement("span");
span.setAttribute("part", "label");
root.appendChild(span);
}
});
customElements.define("outer-host", class extends HTMLElement {
connectedCallback() {
const root = this.attachShadow({ mode: "open" });
const inner = document.createElement("inner-host");
inner.setAttribute("exportparts", "label: inner-label");
root.appendChild(inner);
}
});
test(() => {
const host = document.getElementById("host-part");
const part = host.shadowRoot.querySelector("[part='label']");
assert_equals(getComputedStyle(part).color, "rgb(0, 128, 0)",
"::part() attr() reads data-color from host");
}, "::part() attr() resolves on shadow host");
test(() => {
const host = document.getElementById("host-part");
const part = host.shadowRoot.querySelector("[part='label']");
assert_equals(getComputedStyle(part).fontSize, "32px",
"::part() attr() reads data-size from host");
}, "::part() attr() with percentage type resolves on shadow host");
test(() => {
const host = document.getElementById("host-part");
const part = host.shadowRoot.querySelector("[part='label']");
host.setAttribute("data-color", "blue");
assert_equals(getComputedStyle(part).color, "rgb(0, 0, 255)",
"::part() attr() invalidates when host attribute changes");
}, "::part() attr() invalidation on host attribute change");
test(() => {
const outer = document.getElementById("host-outer");
const inner = outer.shadowRoot.querySelector("inner-host");
const part = inner.shadowRoot.querySelector("[part='label']");
assert_equals(getComputedStyle(part).color, "rgb(0, 128, 0)",
"::part() across two shadow boundaries reads from the outer host");
}, "::part() attr() resolves on the originating host across exportparts");
test(() => {
const outer = document.getElementById("host-outer");
const inner = outer.shadowRoot.querySelector("inner-host");
const part = inner.shadowRoot.querySelector("[part='label']");
outer.setAttribute("data-color", "blue");
assert_equals(getComputedStyle(part).color, "rgb(0, 0, 255)",
"::part() across two shadow boundaries invalidates on outer host attribute change");
}, "::part() attr() invalidation across exportparts");
test(() => {
const slotted = document.querySelector("#slotted-host > span");
slotted.setAttribute("data-color", "blue");
assert_equals(getComputedStyle(slotted).color, "rgb(0, 0, 255)",
"::slotted() attr() invalidates when the slotted element's attribute changes");
}, "::slotted() attr() invalidation on the slotted element");
</script>