has-in-parent-position.html (11049B)
1 <!DOCTYPE html> 2 <meta charset="utf-8"> 3 <title>CSS Selector Invalidation: :has() in parent position</title> 4 <link rel="author" title="Antti Koivisto" href="mailto:antti@apple.com"> 5 <script src="/resources/testharness.js"></script> 6 <script src="/resources/testharnessreport.js"></script> 7 <link rel="help" href="https://drafts.csswg.org/selectors/#relational"> 8 <style> 9 div, main { color: grey } 10 div:has(.test) > #subject { color: red } 11 div:has([test_attr]) > #subject { color: orangered } 12 div:has(> .test) > #subject { color: green } 13 div:has(> [test_attr]) > #subject { color: lightgreen } 14 div:has(~ .test) > #subject { color: yellow } 15 div:has(~ [test_attr]) > #subject { color: ivory } 16 div:has(+ .test) > #subject { color: blue } 17 div:has(+ [test_attr]) > #subject { color: skyblue } 18 div:has(~ div .test) > #subject { color: purple } 19 div:has(~ div [test_attr]) > #subject { color: violet } 20 div:has(+ div .test) > #subject { color: pink } 21 div:has(+ div [test_attr]) > #subject { color: lightpink } 22 </style> 23 24 <main id=main> 25 <div id=subject_ancestor> 26 <div id=subject_parent> 27 <div id=subject> 28 <div id=subject_child> 29 <div id=subject_descendant></div> 30 </div> 31 </div> 32 </div> 33 </div> 34 </main> 35 36 <script> 37 const grey = 'rgb(128, 128, 128)'; 38 const red = 'rgb(255, 0, 0)'; 39 const orangered = 'rgb(255, 69, 0)'; 40 const green = 'rgb(0, 128, 0)'; 41 const lightgreen = 'rgb(144, 238, 144)'; 42 const blue = 'rgb(0, 0, 255)'; 43 const skyblue = 'rgb(135, 206, 235)'; 44 const yellow = 'rgb(255, 255, 0)'; 45 const ivory = 'rgb(255, 255, 240)'; 46 const purple = 'rgb(128, 0, 128)'; 47 const violet = 'rgb(238, 130, 238)'; 48 const pink = 'rgb(255, 192, 203)'; 49 const lightpink = 'rgb(255, 182, 193)'; 50 const colors = { 51 grey: { 52 classTest: grey, 53 attributeTest: grey, 54 }, 55 red: { 56 classTest: red, 57 attributeTest: orangered, 58 }, 59 green: { 60 classTest: green, 61 attributeTest: lightgreen, 62 }, 63 blue: { 64 classTest: blue, 65 attributeTest: skyblue, 66 }, 67 yellow: { 68 classTest: yellow, 69 attributeTest: ivory, 70 }, 71 purple: { 72 classTest: purple, 73 attributeTest: violet, 74 }, 75 pink: { 76 classTest: pink, 77 attributeTest: lightpink, 78 }, 79 }; 80 81 function testColor(test_name, color) { 82 test(function() { 83 assert_equals(getComputedStyle(subject).color, color); 84 }, test_name); 85 } 86 87 function testClassChange(element, expectedColorName) 88 { 89 const expectedColorForClassTest = colors[expectedColorName].classTest; 90 element.classList.add('test'); 91 testColor(`add .test to ${element.id}`, expectedColorForClassTest); 92 element.classList.remove('test'); 93 testColor(`remove .test from ${element.id}`, grey); 94 } 95 96 function testElementInsertionBefore(beforeElement, expectedColorName) 97 { 98 const expectedColorForClassTest = colors[expectedColorName].classTest; 99 const expectedColorForAttributeTest = colors[expectedColorName].attributeTest; 100 const newElement = document.createElement('div'); 101 newElement.classList.add('test'); 102 103 beforeElement.before(newElement); 104 testColor(`insert element div.test before ${beforeElement.id}`, expectedColorForClassTest); 105 106 newElement.classList.remove('test'); 107 testColor(`remove the class 'test' from the element inserted before ${beforeElement.id}`, grey); 108 109 newElement.classList.add('test'); 110 testColor(`add the class 'test' again to the element inserted before ${beforeElement.id}`, expectedColorForClassTest); 111 112 newElement.remove(); 113 testColor(`remove element div.test before ${beforeElement.id}`, grey); 114 115 newElement.classList.remove('test'); 116 117 beforeElement.before(newElement); 118 testColor(`insert element div before ${beforeElement.id}`, grey); 119 120 newElement.classList.add('test'); 121 testColor(`add the class 'test' to the element inserted again before ${beforeElement.id}`, expectedColorForClassTest); 122 123 newElement.classList.remove('test'); 124 testColor(`remove the class 'test' from the element inserted again before ${beforeElement.id}`, grey); 125 126 newElement.remove(); 127 testColor(`remove element div before ${beforeElement.id}`, grey); 128 129 newElement.setAttribute('test_attr', ''); 130 131 beforeElement.before(newElement); 132 testColor(`insert element div[test_attr] before ${beforeElement.id}`, expectedColorForAttributeTest); 133 134 newElement.remove(); 135 testColor(`remove element div[test_attr] before ${beforeElement.id}`, grey); 136 } 137 138 function testElementInsertionAfter(afterElement, expectedColorName) 139 { 140 const expectedColorForClassTest = colors[expectedColorName].classTest; 141 const expectedColorForAttributeTest = colors[expectedColorName].attributeTest; 142 const newElement = document.createElement('div'); 143 newElement.classList.add('test'); 144 145 afterElement.after(newElement); 146 testColor(`insert element div.test after ${afterElement.id}`, expectedColorForClassTest); 147 148 newElement.classList.remove('test'); 149 testColor(`remove the class 'test' from the element inserted after ${afterElement.id}`, grey); 150 151 newElement.classList.add('test'); 152 testColor(`add the class 'test' again to the element inserted after ${afterElement.id}`, expectedColorForClassTest); 153 154 newElement.remove(); 155 testColor(`remove element div.test after ${afterElement.id}`, grey); 156 157 newElement.classList.remove('test'); 158 159 afterElement.after(newElement); 160 testColor(`insert element div after ${afterElement.id}`, grey); 161 162 newElement.classList.add('test'); 163 testColor(`add the class 'test' to the element inserted again after ${afterElement.id}`, expectedColorForClassTest); 164 165 newElement.classList.remove('test'); 166 testColor(`remove the class 'test' from the element inserted again after ${afterElement.id}`, grey); 167 168 newElement.remove(); 169 testColor(`remove element div after ${afterElement.id}`, grey); 170 171 newElement.setAttribute('test_attr', ''); 172 173 afterElement.after(newElement); 174 testColor(`insert element div[test_attr] after ${afterElement.id}`, expectedColorForAttributeTest); 175 176 newElement.remove(); 177 testColor(`remove element div[test_attr] after ${afterElement.id}`, grey); 178 } 179 180 function testTreeInsertionBefore(beforeElement, expectedColorName) 181 { 182 const expectedColorForClassTest = colors[expectedColorName].classTest; 183 const expectedColorForAttributeTest = colors[expectedColorName].attributeTest; 184 const newElement = document.createElement('div'); 185 const newChild = document.createElement('div'); 186 newChild.classList.add('test'); 187 newElement.appendChild(newChild); 188 189 beforeElement.before(newElement); 190 testColor(`insert tree div>div.test before ${beforeElement.id}`, expectedColorForClassTest); 191 192 newChild.classList.remove('test'); 193 testColor(`remove the class 'test' from the element in the tree inserted before ${beforeElement.id}`, grey); 194 195 newChild.classList.add('test'); 196 testColor(`add the class 'test' again to the element in the tree inserted before ${beforeElement.id}`, expectedColorForClassTest); 197 198 newElement.remove(); 199 testColor(`remove tree div>div.test before ${beforeElement.id}`, grey); 200 201 newChild.classList.remove('test'); 202 203 beforeElement.before(newElement); 204 testColor(`insert tree div>div before ${beforeElement.id}`, grey); 205 206 newChild.classList.add('test'); 207 testColor(`add the class 'test' to the element in the tree inserted again before ${beforeElement.id}`, expectedColorForClassTest); 208 209 newChild.classList.remove('test'); 210 testColor(`remove the class 'test' from the element in the tree inserted again before ${beforeElement.id}`, grey); 211 212 newElement.remove(); 213 testColor(`remove tree div>div before ${beforeElement.id}`, grey); 214 215 newChild.setAttribute('test_attr', ''); 216 217 beforeElement.before(newElement); 218 testColor(`insert element div>div[test_attr] before ${beforeElement.id}`, expectedColorForAttributeTest); 219 220 newElement.remove(); 221 testColor(`remove element div>div[test_attr] before ${beforeElement.id}`, grey); 222 } 223 224 function testTreeInsertionAfter(afterElement, expectedColorName) 225 { 226 const expectedColorForClassTest = colors[expectedColorName].classTest; 227 const expectedColorForAttributeTest = colors[expectedColorName].attributeTest; 228 const newElement = document.createElement('div'); 229 const newChild = document.createElement('div'); 230 newChild.classList.add('test'); 231 newElement.appendChild(newChild); 232 233 afterElement.after(newElement); 234 testColor(`insert tree div>div.test after ${afterElement.id}`, expectedColorForClassTest); 235 236 newChild.classList.remove('test'); 237 testColor(`remove the class 'test' from the element in the tree inserted after ${afterElement.id}`, grey); 238 239 newChild.classList.add('test'); 240 testColor(`add the class 'test' again to the element in the tree inserted after ${afterElement.id}`, expectedColorForClassTest); 241 242 newElement.remove(); 243 testColor(`remove tree div>div.test after ${afterElement.id}`, grey); 244 245 newChild.classList.remove('test'); 246 247 afterElement.after(newElement); 248 testColor(`insert tree div>div after ${afterElement.id}`, grey); 249 250 newChild.classList.add('test'); 251 testColor(`add the class 'test' to the element in the tree inserted again after ${afterElement.id}`, expectedColorForClassTest); 252 253 newChild.classList.remove('test'); 254 testColor(`remove the class 'test' from the element in the tree inserted again after ${afterElement.id}`, grey); 255 256 newElement.remove(); 257 testColor(`remove tree div>div after ${afterElement.id}`, grey); 258 259 newChild.setAttribute('test_attr', ''); 260 261 afterElement.after(newElement); 262 testColor(`insert element div>div[test_attr] after ${afterElement.id}`, expectedColorForAttributeTest); 263 264 newElement.remove(); 265 testColor(`remove element div>div[test_attr] after ${afterElement.id}`, grey); 266 } 267 268 testColor('Initial color', grey); 269 270 testClassChange(subject_ancestor, "grey"); 271 testClassChange(subject_parent, "grey"); 272 testClassChange(subject, "green"); 273 testClassChange(subject_child, "red"); 274 testClassChange(subject_descendant, "red"); 275 276 testElementInsertionBefore(subject_ancestor, "grey"); 277 testElementInsertionBefore(subject_parent, "grey"); 278 testElementInsertionBefore(subject, "green"); 279 testElementInsertionBefore(subject_child, "red"); 280 testElementInsertionBefore(subject_descendant, "red"); 281 282 testElementInsertionAfter(subject_ancestor, "grey"); 283 testElementInsertionAfter(subject_parent, "blue"); 284 testElementInsertionAfter(subject, "green"); 285 testElementInsertionAfter(subject_child, "red"); 286 testElementInsertionAfter(subject_descendant, "red"); 287 288 testTreeInsertionBefore(subject_ancestor, "grey"); 289 testTreeInsertionBefore(subject_parent, "grey"); 290 testTreeInsertionBefore(subject, "red"); 291 testTreeInsertionBefore(subject_child, "red"); 292 testTreeInsertionBefore(subject_descendant, "red"); 293 294 testTreeInsertionAfter(subject_ancestor, "grey"); 295 testTreeInsertionAfter(subject_parent, "pink"); 296 testTreeInsertionAfter(subject, "red"); 297 testTreeInsertionAfter(subject_child, "red"); 298 testTreeInsertionAfter(subject_descendant, "red"); 299 300 </script>