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