872 lines
19 KiB
HTML
872 lines
19 KiB
HTML
<!DOCTYPE html>
|
|
<title>@scope - invalidation</title>
|
|
<link rel="help" href="https://drafts.csswg.org/css-cascade-6/#scope-atrule">
|
|
<script src="/resources/testharness.js"></script>
|
|
<script src="/resources/testharnessreport.js"></script>
|
|
<script>
|
|
|
|
function test_scope_invalidation(script_element, callback_fn, description) {
|
|
test((t) => {
|
|
// The provided <script> element must be an immedate subsequent sibling of
|
|
// a <template> element.
|
|
let template_element = script_element.previousElementSibling;
|
|
assert_equals(template_element.tagName, 'TEMPLATE');
|
|
|
|
t.add_cleanup(() => {
|
|
while (main.firstChild)
|
|
main.firstChild.remove()
|
|
});
|
|
|
|
main.append(template_element.content.cloneNode(true));
|
|
|
|
callback_fn();
|
|
}, description);
|
|
}
|
|
|
|
function assert_green(element) {
|
|
assert_equals(getComputedStyle(element).backgroundColor, 'rgb(0, 128, 0)');
|
|
}
|
|
function assert_red(element) {
|
|
assert_equals(getComputedStyle(element).backgroundColor, 'rgb(255, 0, 0)');
|
|
}
|
|
function assert_not_green(element) {
|
|
assert_equals(getComputedStyle(element).backgroundColor, 'rgb(0, 0, 0)');
|
|
}
|
|
</script>
|
|
<style>
|
|
main * {
|
|
background-color: black;
|
|
}
|
|
</style>
|
|
<main id=main>
|
|
</main>
|
|
|
|
<!-- Tests follow -->
|
|
|
|
<template>
|
|
<style>
|
|
@scope (.a) {
|
|
span { background-color: green; }
|
|
}
|
|
</style>
|
|
<div>
|
|
<span></span>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
test_scope_invalidation(document.currentScript, () => {
|
|
let div = main.querySelector('div');
|
|
let span = main.querySelector('div > span');
|
|
assert_not_green(span);
|
|
div.classList.add('a');
|
|
assert_green(span);
|
|
div.classList.remove('a');
|
|
assert_not_green(span);
|
|
}, 'Element becoming scope root');
|
|
</script>
|
|
|
|
<template>
|
|
<style>
|
|
@scope (.a, .b) {
|
|
span { background-color: green; }
|
|
}
|
|
</style>
|
|
<div>
|
|
<span></span>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
test_scope_invalidation(document.currentScript, () => {
|
|
let div = main.querySelector('div');
|
|
let span = main.querySelector('div > span');
|
|
|
|
// .a
|
|
assert_not_green(span);
|
|
div.classList.add('a');
|
|
assert_green(span);
|
|
div.classList.remove('a');
|
|
assert_not_green(span);
|
|
|
|
// .b
|
|
assert_not_green(span);
|
|
div.classList.add('b');
|
|
assert_green(span);
|
|
div.classList.remove('b');
|
|
assert_not_green(span);
|
|
}, 'Element becoming scope root (selector list)');
|
|
</script>
|
|
|
|
<template>
|
|
<style>
|
|
@scope (.a) {
|
|
:scope { background-color: green; }
|
|
}
|
|
</style>
|
|
<div class=b></div>
|
|
</template>
|
|
<script>
|
|
test_scope_invalidation(document.currentScript, () => {
|
|
let b = main.querySelector('.b');
|
|
assert_not_green(b);
|
|
b.classList.add('a');
|
|
assert_green(b);
|
|
b.classList.remove('a');
|
|
assert_not_green(b);
|
|
}, 'Element becoming scope root, with inner :scope rule');
|
|
</script>
|
|
|
|
<template>
|
|
<style>
|
|
@scope (.a) to (.b) {
|
|
span { background-color: green; }
|
|
}
|
|
</style>
|
|
<div class=a>
|
|
<div>
|
|
<span></span>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
test_scope_invalidation(document.currentScript, () => {
|
|
let inner_div = main.querySelector('.a > div');
|
|
let span = main.querySelector('.a > div > span');
|
|
assert_green(span);
|
|
inner_div.classList.add('b');
|
|
assert_not_green(span);
|
|
inner_div.classList.remove('b');
|
|
assert_green(span);
|
|
}, 'Parent element becoming scope limit');
|
|
</script>
|
|
|
|
<template>
|
|
<style>
|
|
@scope (.a) to (.b, .c) {
|
|
span { background-color: green; }
|
|
}
|
|
</style>
|
|
<div class=a>
|
|
<div>
|
|
<span></span>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
test_scope_invalidation(document.currentScript, () => {
|
|
let inner_div = main.querySelector('.a > div');
|
|
let span = main.querySelector('.a > div > span');
|
|
|
|
// .b
|
|
assert_green(span);
|
|
inner_div.classList.add('b');
|
|
assert_not_green(span);
|
|
inner_div.classList.remove('b');
|
|
assert_green(span);
|
|
|
|
// .c
|
|
assert_green(span);
|
|
inner_div.classList.add('c');
|
|
assert_not_green(span);
|
|
inner_div.classList.remove('c');
|
|
assert_green(span);
|
|
}, 'Parent element becoming scope limit (selector list)');
|
|
</script>
|
|
|
|
<template>
|
|
<style>
|
|
@scope (.a) to (.b) {
|
|
span { background-color: green; }
|
|
}
|
|
</style>
|
|
<div class=a>
|
|
<div>
|
|
<span></span>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
test_scope_invalidation(document.currentScript, () => {
|
|
let span = main.querySelector('.a > div > span');
|
|
assert_green(span);
|
|
span.classList.add('b');
|
|
assert_not_green(span);
|
|
span.classList.remove('b');
|
|
assert_green(span);
|
|
}, 'Subject element becoming scope limit');
|
|
</script>
|
|
|
|
<template>
|
|
<style>
|
|
@scope (.a) to (.b .c) {
|
|
span { background-color: green; }
|
|
}
|
|
</style>
|
|
<div class=a>
|
|
<div>
|
|
<div class=c>
|
|
<span></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
test_scope_invalidation(document.currentScript, () => {
|
|
let intermediate_div = main.querySelector('.a > div');
|
|
let span = main.querySelector('span');
|
|
assert_green(span);
|
|
intermediate_div.classList.add('b');
|
|
assert_not_green(span);
|
|
intermediate_div.classList.remove('b');
|
|
assert_green(span);
|
|
}, 'Parent element affecting scope limit');
|
|
</script>
|
|
|
|
<template>
|
|
<style>
|
|
@scope (.a) to (.b ~ .c) {
|
|
span { background-color: green; }
|
|
}
|
|
</style>
|
|
<div class=a>
|
|
<div></div>
|
|
<div></div>
|
|
<div></div>
|
|
<div></div>
|
|
<div class=c>
|
|
<span></span>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
test_scope_invalidation(document.currentScript, () => {
|
|
let sibling_div = main.querySelector('.a > div');
|
|
let span = main.querySelector('span');
|
|
assert_green(span);
|
|
sibling_div.classList.add('b');
|
|
assert_not_green(span);
|
|
sibling_div.classList.remove('b');
|
|
assert_green(span);
|
|
}, 'Sibling element affecting scope limit');
|
|
</script>
|
|
|
|
<template>
|
|
<style>
|
|
@scope (.a) {
|
|
@scope (.b) {
|
|
span { background-color: green; }
|
|
}
|
|
}
|
|
</style>
|
|
<div>
|
|
<div>
|
|
<span></span>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
test_scope_invalidation(document.currentScript, () => {
|
|
let outer_div = main.querySelector(':scope > div');
|
|
let inner_div = main.querySelector(':scope > div > div');
|
|
let span = main.querySelector('div > div > span');
|
|
|
|
assert_not_green(span);
|
|
|
|
outer_div.classList.add('a');
|
|
assert_not_green(span);
|
|
|
|
inner_div.classList.add('b');
|
|
assert_green(span);
|
|
|
|
// Toggle .b while .a remains.
|
|
inner_div.classList.remove('b');
|
|
assert_not_green(span);
|
|
inner_div.classList.add('b');
|
|
assert_green(span);
|
|
|
|
// Toggle .a while .b remains.
|
|
outer_div.classList.remove('a');
|
|
assert_not_green(span);
|
|
outer_div.classList.add('a');
|
|
assert_green(span);
|
|
}, 'Toggling inner/outer scope roots');
|
|
</script>
|
|
|
|
|
|
<template>
|
|
<style>
|
|
@scope (.a) {
|
|
:scope { background-color:green; }
|
|
}
|
|
</style>
|
|
<div></div>
|
|
</template>
|
|
<script>
|
|
test_scope_invalidation(document.currentScript, () => {
|
|
let div = main.querySelector('main > div');
|
|
assert_not_green(div);
|
|
div.classList.add('a');
|
|
assert_green(div);
|
|
div.classList.remove('a');
|
|
assert_not_green(div);
|
|
}, 'Element becoming root, with :scope in subject');
|
|
</script>
|
|
|
|
|
|
<template>
|
|
<style>
|
|
@scope (.a:has(.c)) {
|
|
.b { background-color:green; }
|
|
}
|
|
</style>
|
|
<div class=a>
|
|
<div class=b>
|
|
<div></div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
test_scope_invalidation(document.currentScript, () => {
|
|
let b = main.querySelector('.b');
|
|
let innermost = main.querySelector('.b > div');
|
|
assert_not_green(b);
|
|
innermost.classList.add('c');
|
|
assert_green(b);
|
|
innermost.classList.remove('c');
|
|
assert_not_green(b);
|
|
}, 'Scope root with :has()');
|
|
</script>
|
|
|
|
|
|
<template>
|
|
<style>
|
|
@scope (.a:has(.c)) {
|
|
:scope { background-color:green; }
|
|
}
|
|
</style>
|
|
<div class=a>
|
|
<div class=b>
|
|
<div></div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
test_scope_invalidation(document.currentScript, () => {
|
|
let a = main.querySelector('.a');
|
|
let innermost = main.querySelector('.b > div');
|
|
assert_not_green(a);
|
|
innermost.classList.add('c');
|
|
assert_green(a);
|
|
innermost.classList.remove('c');
|
|
assert_not_green(a);
|
|
}, 'Scope root with :has(), :scope subject');
|
|
</script>
|
|
|
|
|
|
<template>
|
|
<style>
|
|
@scope (.a:has(.c)) {
|
|
:scope { background-color:green; }
|
|
:scope .b { background-color:green; }
|
|
}
|
|
</style>
|
|
<div class=a>
|
|
<div class=b>
|
|
<div></div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
test_scope_invalidation(document.currentScript, () => {
|
|
let a = main.querySelector('.a');
|
|
let b = main.querySelector('.b');
|
|
let innermost = main.querySelector('.b > div');
|
|
assert_not_green(a);
|
|
assert_not_green(b);
|
|
innermost.classList.add('c');
|
|
assert_green(a);
|
|
assert_green(b);
|
|
innermost.classList.remove('c');
|
|
assert_not_green(a);
|
|
assert_not_green(b);
|
|
}, 'Scope root with :has(), :scope both subject and non-subject');
|
|
</script>
|
|
|
|
|
|
<template>
|
|
<style>
|
|
@scope (.a) to (.b:has(.c)) {
|
|
.b { background-color:green; }
|
|
}
|
|
</style>
|
|
<div class=a>
|
|
<div class=b>
|
|
<div></div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
test_scope_invalidation(document.currentScript, () => {
|
|
let b = main.querySelector('.b');
|
|
let innermost = main.querySelector('.b > div');
|
|
assert_green(b);
|
|
innermost.classList.add('c');
|
|
assert_not_green(b);
|
|
innermost.classList.remove('c');
|
|
assert_green(b);
|
|
}, 'Scope limit with :has()');
|
|
</script>
|
|
|
|
<template>
|
|
<style>
|
|
@scope (.a) {
|
|
.b ~ :scope { background-color:green; }
|
|
}
|
|
</style>
|
|
<div></div>
|
|
<div></div>
|
|
</template>
|
|
<script>
|
|
test_scope_invalidation(document.currentScript, () => {
|
|
let div1 = main.querySelector('main > div:nth-of-type(1)');
|
|
let div2 = main.querySelector('main > div:nth-of-type(2)');
|
|
|
|
assert_not_green(div2);
|
|
div1.classList.add('b');
|
|
assert_not_green(div2);
|
|
div2.classList.add('a');
|
|
assert_green(div2);
|
|
div1.classList.remove('b');
|
|
assert_not_green(div2);
|
|
}, 'Element becoming root, with :scope selected by ~ combinator');
|
|
</script>
|
|
|
|
<template>
|
|
<style>
|
|
@scope (.a ~ .b) {
|
|
.c { background-color:green; }
|
|
}
|
|
</style>
|
|
<div>
|
|
<div></div>
|
|
<div></div>
|
|
<div></div>
|
|
<div class=b>
|
|
<div class=c></div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
test_scope_invalidation(document.currentScript, () => {
|
|
let root = main.querySelector('div > div:first-child');
|
|
let c = main.querySelector('.c');
|
|
assert_not_green(c);
|
|
root.classList.add('a');
|
|
assert_green(c);
|
|
root.classList.remove('a');
|
|
assert_not_green(c);
|
|
}, 'Element becoming root via ~ combinator');
|
|
</script>
|
|
|
|
<template>
|
|
<style>
|
|
@scope (.a + .b) {
|
|
.c { background-color:green; }
|
|
}
|
|
</style>
|
|
<div>
|
|
<div></div>
|
|
<div class=b>
|
|
<div class=c></div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
test_scope_invalidation(document.currentScript, () => {
|
|
let root = main.querySelector('div > div:first-child');
|
|
let c = main.querySelector('.c');
|
|
assert_not_green(c);
|
|
root.classList.add('a');
|
|
assert_green(c);
|
|
root.classList.remove('a');
|
|
assert_not_green(c);
|
|
}, 'Element becoming root via + combinator');
|
|
</script>
|
|
|
|
<template>
|
|
<style>
|
|
@scope (.root) {
|
|
:not(:scope) { background-color:green; }
|
|
}
|
|
</style>
|
|
<div class=root>
|
|
<div class=a></div>
|
|
<div class=b></div>
|
|
<div class=c></div>
|
|
</div>
|
|
<div class=a></div>
|
|
</template>
|
|
<script>
|
|
test_scope_invalidation(document.currentScript, () => {
|
|
let root = main.querySelector('.root');
|
|
let a1 = main.querySelector('.root > .a');
|
|
let b = main.querySelector('.root > .b');
|
|
let c = main.querySelector('.root > .c');
|
|
let a2 = main.querySelector('main > .a');
|
|
|
|
assert_not_green(root);
|
|
assert_green(a1);
|
|
assert_green(b);
|
|
assert_green(c);
|
|
assert_not_green(a2);
|
|
|
|
root.classList.remove('root');
|
|
assert_not_green(root);
|
|
assert_not_green(a1);
|
|
assert_not_green(b);
|
|
assert_not_green(c);
|
|
assert_not_green(a2);
|
|
|
|
root.classList.add('root');
|
|
assert_not_green(root);
|
|
assert_green(a1);
|
|
assert_green(b);
|
|
assert_green(c);
|
|
assert_not_green(a2);
|
|
}, ':not(scope) in subject');
|
|
</script>
|
|
|
|
<template>
|
|
<style>
|
|
@scope (.root) {
|
|
:not(:scope) > .a { background-color:green; }
|
|
}
|
|
</style>
|
|
<div class=root>
|
|
<div class=a></div>
|
|
<div>
|
|
<div class=a></div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
test_scope_invalidation(document.currentScript, () => {
|
|
let root = main.querySelector('.root');
|
|
let outer_a = main.querySelector('.root > .a');
|
|
let inner_a = main.querySelector('.root > div > .a');
|
|
|
|
assert_not_green(outer_a);
|
|
assert_green(inner_a);
|
|
|
|
root.classList.remove('root');
|
|
assert_not_green(outer_a);
|
|
assert_not_green(inner_a);
|
|
|
|
root.classList.add('root');
|
|
assert_not_green(outer_a);
|
|
assert_green(inner_a);
|
|
}, ':not(scope) in ancestor');
|
|
</script>
|
|
|
|
<template>
|
|
<style>
|
|
@scope (.root) to (:not(:scope)) {
|
|
:is(div, :scope) { background-color: green; }
|
|
}
|
|
</style>
|
|
<div class=root>
|
|
<div class=a></div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
test_scope_invalidation(document.currentScript, () => {
|
|
let root = main.querySelector('.root');
|
|
let a = main.querySelector('.root > .a');
|
|
|
|
assert_green(root);
|
|
assert_not_green(a);
|
|
|
|
root.classList.remove('root');
|
|
assert_not_green(root);
|
|
assert_not_green(a);
|
|
|
|
root.classList.add('root');
|
|
assert_green(root);
|
|
assert_not_green(a);
|
|
}, ':not(scope) in limit subject');
|
|
</script>
|
|
|
|
<template>
|
|
<style>
|
|
@scope (.root) to (:not(:scope) > .a) {
|
|
:is(div, :scope) { background-color: green; }
|
|
}
|
|
</style>
|
|
<div class=root>
|
|
<div class=a>
|
|
<div class=a></div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
test_scope_invalidation(document.currentScript, () => {
|
|
let root = main.querySelector('.root');
|
|
let outer_a = main.querySelector('.root > .a');
|
|
let inner_a = main.querySelector('.root > .a > .a');
|
|
|
|
assert_green(root);
|
|
assert_green(outer_a);
|
|
assert_not_green(inner_a);
|
|
|
|
root.classList.remove('root');
|
|
assert_not_green(root);
|
|
assert_not_green(outer_a);
|
|
assert_not_green(inner_a);
|
|
|
|
root.classList.add('root');
|
|
assert_green(root);
|
|
assert_green(outer_a);
|
|
assert_not_green(inner_a);
|
|
}, ':not(scope) in limit ancestor');
|
|
</script>
|
|
|
|
<template>
|
|
<style>
|
|
@scope (:nth-child(2n of .a)) {
|
|
:scope { background-color: green; }
|
|
}
|
|
</style>
|
|
<div id=wrapper>
|
|
<div class=a></div>
|
|
<div></div>
|
|
<div class=a></div>
|
|
<div></div>
|
|
<div class=a></div>
|
|
<div></div>
|
|
<div class=a></div>
|
|
<div></div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
test_scope_invalidation(document.currentScript, () => {
|
|
let e = main.querySelectorAll('#wrapper > div');
|
|
assert_equals(e.length, 8);
|
|
|
|
// <div class=a></div>
|
|
// <div></div>
|
|
// <div class=a></div>
|
|
// <div></div>
|
|
// <div class=a></div>
|
|
// <div></div>
|
|
// <div class=a></div>
|
|
// <div></div>
|
|
assert_not_green(e[0]);
|
|
assert_not_green(e[1]);
|
|
assert_green(e[2]);
|
|
assert_not_green(e[3]);
|
|
assert_not_green(e[4]);
|
|
assert_not_green(e[5]);
|
|
assert_green(e[6]);
|
|
assert_not_green(e[7]);
|
|
|
|
e[1].classList.add('a');
|
|
// <div class=a></div>
|
|
// <div class=a></div>
|
|
// <div class=a></div>
|
|
// <div></div>
|
|
// <div class=a></div>
|
|
// <div></div>
|
|
// <div class=a></div>
|
|
// <div></div>
|
|
assert_not_green(e[0]);
|
|
assert_green(e[1]);
|
|
assert_not_green(e[2]);
|
|
assert_not_green(e[3]);
|
|
assert_green(e[4]);
|
|
assert_not_green(e[5]);
|
|
assert_not_green(e[6]);
|
|
assert_not_green(e[7]);
|
|
|
|
e[1].classList.remove('a');
|
|
// <div class=a></div>
|
|
// <div></div>
|
|
// <div class=a></div>
|
|
// <div></div>
|
|
// <div class=a></div>
|
|
// <div></div>
|
|
// <div class=a></div>
|
|
// <div></div>
|
|
assert_not_green(e[0]);
|
|
assert_not_green(e[1]);
|
|
assert_green(e[2]);
|
|
assert_not_green(e[3]);
|
|
assert_not_green(e[4]);
|
|
assert_not_green(e[5]);
|
|
assert_green(e[6]);
|
|
assert_not_green(e[7]);
|
|
}, ':nth-child() in scope root');
|
|
</script>
|
|
|
|
<template>
|
|
<style>
|
|
@scope (#wrapper) to (:nth-child(4n of .a)) {
|
|
div { background-color: green; }
|
|
}
|
|
</style>
|
|
<div id=wrapper>
|
|
<div class=a></div>
|
|
<div></div>
|
|
<div class=a></div>
|
|
<div></div>
|
|
<div class=a></div>
|
|
<div></div>
|
|
<div class=a></div>
|
|
<div></div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
test_scope_invalidation(document.currentScript, () => {
|
|
let e = main.querySelectorAll('#wrapper > div');
|
|
assert_equals(e.length, 8);
|
|
|
|
// <div class=a></div>
|
|
// <div></div>
|
|
// <div class=a></div>
|
|
// <div></div>
|
|
// <div class=a></div>
|
|
// <div></div>
|
|
// <div class=a></div> <= limit
|
|
// <div></div>
|
|
assert_green(e[0]);
|
|
assert_green(e[1]);
|
|
assert_green(e[2]);
|
|
assert_green(e[3]);
|
|
assert_green(e[4]);
|
|
assert_green(e[5]);
|
|
assert_not_green(e[6]);
|
|
assert_green(e[7]);
|
|
|
|
e[1].classList.add('a');
|
|
// <div class=a></div>
|
|
// <div class=a></div>
|
|
// <div class=a></div>
|
|
// <div></div>
|
|
// <div class=a></div> <= limit
|
|
// <div></div>
|
|
// <div class=a></div>
|
|
// <div></div>
|
|
assert_green(e[0]);
|
|
assert_green(e[1]);
|
|
assert_green(e[2]);
|
|
assert_green(e[3]);
|
|
assert_not_green(e[4]);
|
|
assert_green(e[5]);
|
|
assert_green(e[6]);
|
|
assert_green(e[7]);
|
|
|
|
e[1].classList.remove('a');
|
|
// <div class=a></div>
|
|
// <div></div>
|
|
// <div class=a></div>
|
|
// <div></div>
|
|
// <div class=a></div>
|
|
// <div></div>
|
|
// <div class=a></div> <= limit
|
|
// <div></div>
|
|
assert_green(e[0]);
|
|
assert_green(e[1]);
|
|
assert_green(e[2]);
|
|
assert_green(e[3]);
|
|
assert_green(e[4]);
|
|
assert_green(e[5]);
|
|
assert_not_green(e[6]);
|
|
assert_green(e[7]);
|
|
}, ':nth-child() in scope limit');
|
|
|
|
</script>
|
|
|
|
<template>
|
|
<style>
|
|
@scope (.a) {
|
|
.nomatch { background-color: green; }
|
|
}
|
|
</style>
|
|
<div id=wrapper>
|
|
<div class=a>
|
|
<div class=b></div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
test_scope_invalidation(document.currentScript, () => {
|
|
let b = main.querySelector('.b');
|
|
assert_not_green(b);
|
|
let scope_rule = main.querySelector('style').sheet.cssRules[0];
|
|
assert_true(scope_rule instanceof CSSScopeRule);
|
|
scope_rule.cssRules[0].selectorText = '.b';
|
|
assert_green(b);
|
|
}, 'Modifying selectorText invalidates affected elements');
|
|
</script>
|
|
|
|
<template>
|
|
<style>
|
|
@scope (.a) {
|
|
.nomatch { background-color: green; }
|
|
}
|
|
</style>
|
|
<div id=wrapper>
|
|
<div class=a>
|
|
<div class=b></div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
test_scope_invalidation(document.currentScript, () => {
|
|
let b = main.querySelector('.b');
|
|
assert_not_green(b);
|
|
let scope_rule = main.querySelector('style').sheet.cssRules[0];
|
|
assert_true(scope_rule instanceof CSSScopeRule);
|
|
scope_rule.cssRules[0].selectorText = '> .b';
|
|
assert_green(b);
|
|
}, 'Modifying selectorText invalidates affected elements (>)');
|
|
</script>
|
|
|
|
<template>
|
|
<style>
|
|
.a {
|
|
> .b, > .c {
|
|
background-color: green; /* Specificity: (0, 2, 0) */
|
|
}
|
|
}
|
|
@scope (.a.a) {
|
|
.nomatch1 {
|
|
background-color: red; /* Specificity: (0, 1, 0) */
|
|
}
|
|
.nomatch2 {
|
|
background-color: red; /* Specificity: (0, 1, 0) */
|
|
}
|
|
}
|
|
</style>
|
|
<div id=wrapper>
|
|
<div class=a>
|
|
<div class=b></div>
|
|
<div class=c></div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<script>
|
|
test_scope_invalidation(document.currentScript, () => {
|
|
let b = main.querySelector('.b');
|
|
let c = main.querySelector('.c');
|
|
assert_green(b);
|
|
assert_green(c);
|
|
let scope_rule = main.querySelector('style').sheet.cssRules[1];
|
|
assert_true(scope_rule instanceof CSSScopeRule);
|
|
// Note: relative selectors imply a :where(:scope) selector to the left,
|
|
// which is observably different from an implicit '&' selector through
|
|
// specificity.
|
|
scope_rule.cssRules[0].selectorText = '> .b'; /* Still (0, 1, 0) */
|
|
scope_rule.cssRules[1].selectorText = '& > .c'; /* (0, 3, 0) */
|
|
assert_green(b);
|
|
assert_red(c);
|
|
}, 'Relative selectors set with selectorText are relative to :scope, not &');
|
|
</script>
|