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