has-pseudoclass-only.html (2800B)
1 <!DOCTYPE html> 2 <title>CSS Selectors Invalidation: :has() containing :empty, :first-child, :last-child, pseudoclasses only</title> 3 <link rel="author" title="David Shin" href="mailto:dshin@mozilla.com"> 4 <link rel="help" href="https://drafts.csswg.org/selectors/#relational"> 5 <link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1931432"> 6 <script src="/resources/testharness.js"></script> 7 <script src="/resources/testharnessreport.js"></script> 8 <style> 9 #subject { 10 color: grey; 11 } 12 13 #subject.empty:has(:empty) { 14 color: red; 15 } 16 17 #subject.first:has(:first-child) { 18 color: orange; 19 } 20 21 #subject.last:has(:last-child) { 22 color: yellow; 23 } 24 25 /* :empty :empty never matches, so use something else. */ 26 #subject.empty-subtree:has(:first-child :empty) { 27 color: green; 28 } 29 30 #subject.first-subtree:has(:first-child :first-child) { 31 color: blue; 32 } 33 34 #subject.last-subtree:has(:last-child :last-child) { 35 color: purple; 36 } 37 </style> 38 <div id="subject"></div> 39 <script> 40 const grey = "rgb(128, 128, 128)"; 41 const red = "rgb(255, 0, 0)"; 42 const orange = "rgb(255, 165, 0)"; 43 const yellow = "rgb(255, 255, 0)"; 44 const green = "rgb(0, 128, 0)"; 45 const blue = 'rgb(0, 0, 255)'; 46 const purple = 'rgb(128, 0, 128)'; 47 48 function testColor(test_name, color) { 49 test(function() { 50 assert_equals(getComputedStyle(subject).color, color); 51 }, test_name); 52 } 53 54 function runTest(test_class, child, insert, color_after_add, desc) { 55 subject.classList.add(test_class); 56 testColor(desc + ' color initial', grey); 57 insert(child); 58 testColor(desc + ' color after adding', color_after_add); 59 subject.replaceChildren(); 60 testColor(desc + ' color after removing', grey); 61 subject.classList.remove(test_class); 62 } 63 64 function createSimpleTree() { 65 const root = document.createElement('div'); 66 root.replaceChildren(document.createElement('div'), document.createElement('div')); 67 return root; 68 } 69 70 const tests = { 71 ':empty': {cls: 'empty', child: document.createElement('div'), color_after_add: red}, 72 ':first-child': {cls: 'first', child: document.createElement('div'), color_after_add: orange}, 73 ':last-child': {cls: 'last', child: document.createElement('div'), color_after_add: yellow}, 74 ':empty subtree': {cls: 'empty-subtree', child: createSimpleTree(), color_after_add: green}, 75 ':first-child subtree': {cls: 'first-subtree', child: createSimpleTree(), color_after_add: blue}, 76 ':last-child subtree': {cls: 'last-subtree', child: createSimpleTree(), color_after_add: purple}, 77 }; 78 79 const element = document.createElement('div'); 80 for (const [t, options] of Object.entries(tests)) { 81 runTest(options.cls, options.child, (e) => subject.appendChild(e), options.color_after_add, t + ' append'); 82 runTest(options.cls, options.child, (e) => subject.insertBefore(e, null), options.color_after_add, t + ' insert'); 83 } 84 </script>