focus-method-delegatesFocus.html (10908B)
1 <!DOCTYPE html> 2 <meta charset="utf-8"> 3 <title>HTML Test: focus() on shadow host with delegatesFocus</title> 4 <script src="/resources/testharness.js"></script> 5 <script src="/resources/testharnessreport.js"></script> 6 <script src="/resources/testdriver.js"></script> 7 <script src="/resources/testdriver-vendor.js"></script> 8 <script src="resources/shadow-utils.js"></script> 9 10 <body> 11 <div id="host"> 12 <div id="slottedToSecondSlot" slot="secondSlot">slottedToSecondSlot</div> 13 <div id="slottedToFirstSlot" slot="firstSlot">slottedToFirstSlot</div> 14 </div> 15 <div id="outside">outside</div> 16 </body> 17 18 <script> 19 const host = document.getElementById("host"); 20 const slottedToSecondSlot = document.getElementById("slottedToSecondSlot"); 21 const slottedToFirstSlot = document.getElementById("slottedToFirstSlot"); 22 const outside = document.getElementById("outside"); 23 24 const shadowRoot = host.attachShadow({ mode: "open", delegatesFocus: true }); 25 const aboveSlots = document.createElement("div"); 26 aboveSlots.innerText = "aboveSlots"; 27 const firstSlot = document.createElement("slot"); 28 firstSlot.name = "firstSlot"; 29 const secondSlot = document.createElement("slot"); 30 secondSlot.name = "secondSlot"; 31 const belowSlots = document.createElement("div"); 32 belowSlots.innerText = "belowSlots"; 33 shadowRoot.appendChild(aboveSlots); 34 shadowRoot.appendChild(firstSlot); 35 shadowRoot.appendChild(secondSlot); 36 shadowRoot.appendChild(belowSlots); 37 38 const elementsInFlatTreeOrder = [host, aboveSlots, firstSlot, 39 slottedToFirstSlot, secondSlot, slottedToSecondSlot, belowSlots, outside]; 40 41 // Final structure: 42 // <div #host> (delegatesFocus=true) 43 // #shadowRoot 44 // <div #aboveSlots> 45 // <slot #firstSlot> 46 // (slotted) <div #slottedToFirstSlot> 47 // <slot #secondSlot> 48 // (slotted) <div #slottedToSecondSlot> 49 // <div #belowSlots> 50 // <div #outside> 51 52 53 function setAllTabIndex(value) { 54 setTabIndex(elementsInFlatTreeOrder, value); 55 } 56 57 function removeAllTabIndex() { 58 removeTabIndex(elementsInFlatTreeOrder); 59 } 60 61 function resetTabIndexAndFocus() { 62 removeAllTabIndex(); 63 resetFocus(document); 64 resetFocus(shadowRoot); 65 } 66 67 test(() => { 68 resetTabIndexAndFocus(); 69 setAllTabIndex(0); 70 // Structure: 71 // <div #host> (delegatesFocus=true) tabindex=0 72 // #shadowRoot 73 // <div #aboveSlots> tabindex=0 74 // <slot #firstSlot> tabindex=0 75 // (slotted) <div #slottedToFirstSlot> tabindex=0 76 // <slot #secondSlot> tabindex=0 77 // (slotted) <div #slottedToSecondSlot> tabindex=0 78 // <div #belowSlots> tabindex=0 79 // <div #outside> tabindex=0 80 // First focusable = #aboveSlots 81 host.focus(); 82 assert_equals(shadowRoot.activeElement, aboveSlots); 83 assert_equals(document.activeElement, host); 84 }, "focus() on host with delegatesFocus, all tabindex=0"); 85 86 test(() => { 87 resetTabIndexAndFocus(); 88 setAllTabIndex(0); 89 setTabIndex([host], -1); 90 // First focusable = #aboveSlots 91 host.focus(); 92 assert_equals(shadowRoot.activeElement, aboveSlots); 93 assert_equals(document.activeElement, host); 94 }, "focus() on host with delegatesFocus & tabindex =-1, all other tabindex=0"); 95 96 test(() => { 97 resetTabIndexAndFocus(); 98 setTabIndex([aboveSlots, slottedToFirstSlot, slottedToSecondSlot, belowSlots], 0); 99 // First focusable = #aboveSlots 100 host.focus(); 101 assert_equals(shadowRoot.activeElement, aboveSlots); 102 assert_equals(document.activeElement, host); 103 }, "focus() on host with delegatesFocus & no tabindex, all other tabindex=0"); 104 105 test(() => { 106 resetTabIndexAndFocus(); 107 setAllTabIndex(-1); 108 setTabIndex([host], 0); 109 // First focusable = #aboveSlots 110 host.focus(); 111 assert_equals(shadowRoot.activeElement, aboveSlots); 112 assert_equals(document.activeElement, host); 113 }, "focus() on host with delegatesFocus & tabindex = 0, all other tabindex=-1"); 114 115 test(() => { 116 resetTabIndexAndFocus(); 117 removeAllTabIndex(); 118 // No focusable element under #host in the flat tree. 119 host.focus(); 120 assert_equals(shadowRoot.activeElement, null); 121 assert_equals(document.activeElement, document.body); 122 }, "focus() on host with delegatesFocus, all without tabindex"); 123 124 test(() => { 125 resetTabIndexAndFocus(); 126 // First focusable = #aboveSlots 127 setAllTabIndex(-1); 128 host.focus(); 129 assert_equals(shadowRoot.activeElement, aboveSlots); 130 assert_equals(document.activeElement, host); 131 }, "focus() on host with delegatesFocus, all tabindex=-1"); 132 133 test(() => { 134 resetTabIndexAndFocus(); 135 removeAllTabIndex(); 136 setTabIndex([host, belowSlots], 0); 137 // Structure: 138 // <div #host> (delegatesFocus=true) tabindex=0 139 // #shadowRoot 140 // <div #aboveSlots> 141 // <slot #firstSlot> 142 // (slotted) <div #slottedToFirstSlot> 143 // <slot #secondSlot> 144 // (slotted) <div #slottedToSecondSlot> 145 // <div #belowSlots> tabindex=0 146 // <div #outside> 147 // First focusable = #belowSlots 148 host.focus(); 149 assert_equals(shadowRoot.activeElement, belowSlots); 150 assert_equals(document.activeElement, host); 151 }, "focus() on host with delegatesFocus & tabindex=0, #belowSlots with tabindex=0"); 152 153 test(() => { 154 resetTabIndexAndFocus(); 155 removeAllTabIndex(); 156 setTabIndex([host, outside], 0); 157 // Structure: 158 // <div #host> (delegatesFocus=true) tabindex=0 159 // #shadowRoot 160 // <div #aboveSlots> 161 // <slot #firstSlot> 162 // (slotted) <div #slottedToFirstSlot> 163 // <slot #secondSlot> 164 // (slotted) <div #slottedToSecondSlot> 165 // <div #belowSlots> 166 // <div #outside> tabindex=0 167 // No focusable element under #host in the flat tree. 168 host.focus(); 169 assert_equals(shadowRoot.activeElement, null); 170 assert_equals(document.activeElement, document.body); 171 }, "focus() on host with delegatesFocus & tabindex=0, #outside with tabindex=0"); 172 173 test(() => { 174 resetTabIndexAndFocus(); 175 setTabIndex([host, aboveSlots, belowSlots], 0); 176 // Structure: 177 // <div #host> (delegatesFocus=true) tabindex=0 178 // #shadowRoot 179 // <div #aboveSlots> tabindex=0 180 // <slot #firstSlot> 181 // (slotted) <div #slottedToFirstSlot> 182 // <slot #secondSlot> 183 // (slotted) <div #slottedToSecondSlot> 184 // <div #belowSlots> tabindex=0 185 // <div #outside> 186 // First focusable = #aboveSlots 187 host.focus(); 188 assert_equals(shadowRoot.activeElement, aboveSlots); 189 assert_equals(document.activeElement, host); 190 }, "focus() on host with delegatesFocus & tabindex=0, #aboveSlots and #belowSlots with tabindex=0"); 191 192 test(() => { 193 resetTabIndexAndFocus(); 194 setTabIndex([host, aboveSlots], 0); 195 setTabIndex([belowSlots], 1); 196 // Structure: 197 // <div #host> (delegatesFocus=true) tabindex=0 198 // #shadowRoot 199 // <div #aboveSlots> tabindex=0 200 // <slot #firstSlot> 201 // (slotted) <div #slottedToFirstSlot> 202 // <slot #secondSlot> 203 // (slotted) <div #slottedToSecondSlot> 204 // <div #belowSlots> tabindex=1 205 // <div #outside> 206 // First focusable = #aboveSlots 207 host.focus(); 208 assert_equals(shadowRoot.activeElement, aboveSlots); 209 assert_equals(document.activeElement, host); 210 }, "focus() on host with delegatesFocus & tabindex=0, #aboveSlots with tabindex=0 and #belowSlots with tabindex=1"); 211 212 test(() => { 213 resetTabIndexAndFocus(); 214 setTabIndex([host, slottedToFirstSlot, slottedToSecondSlot, belowSlots], 0); 215 // Structure: 216 // <div #host> (delegatesFocus=true) tabindex=0 217 // #shadowRoot 218 // <div #aboveSlots> 219 // <slot #firstSlot> 220 // (slotted) <div #slottedToFirstSlot> tabindex=0 221 // <slot #secondSlot> 222 // (slotted) <div #slottedToSecondSlot> tabindex=0 223 // <div #belowSlots> tabindex=0 224 // <div #outside> 225 // First focusable = #slottedToFirstSlot 226 host.focus(); 227 assert_equals(shadowRoot.activeElement, belowSlots); 228 assert_equals(document.activeElement, host); 229 }, "focus() on host with delegatesFocus & tabindex=0, #slottedToFirstSlot, #slottedToSecondSlot, #belowSlots with tabindex=0"); 230 231 test(() => { 232 resetTabIndexAndFocus(); 233 setTabIndex([aboveSlots, belowSlots], 0); 234 belowSlots.focus(); 235 host.focus(); 236 assert_equals(shadowRoot.activeElement, belowSlots); 237 }, "focus() on host with delegatesFocus and already-focused non-first shadow descendant"); 238 239 function createNestedHosts(innerDelegatesFocus) { 240 // Structure: 241 // <div> outerHost 242 // <input> outerLightChild 243 // #shadowRoot outerShadow delegatesFocus=true 244 // <span> innerHost 245 // #shadowRoot inneShadow delegatesFocus=true/false 246 // <input> innerShadowChild 247 // <input> outerShadowChild 248 const outerHost = document.createElement('div'); 249 const outerLightChild = document.createElement('input'); 250 outerHost.appendChild(outerLightChild); 251 const innerHost = document.createElement('span'); 252 const outerShadow = outerHost.attachShadow({mode: 'closed', delegatesFocus:true}); 253 outerShadow.appendChild(innerHost); 254 const outerShadowChild = document.createElement('input'); 255 outerShadow.appendChild(outerShadowChild); 256 257 const innerShadow = innerHost.attachShadow({mode: 'closed', delegatesFocus:innerDelegatesFocus}); 258 const innerShadowChild = document.createElement('input'); 259 innerShadow.appendChild(innerShadowChild); 260 261 document.body.insertBefore(outerHost, document.body.firstChild); 262 return {outerHost: outerHost, 263 outerLightChild: outerLightChild, 264 outerShadow: outerShadow, 265 outerShadowChild: outerShadowChild, 266 innerHost: innerHost, 267 innerShadow: innerShadow, 268 innerShadowChild: innerShadowChild}; 269 } 270 271 test(() => { 272 const dom = createNestedHosts(false); 273 dom.outerHost.focus(); 274 assert_equals(document.activeElement, dom.outerHost); 275 assert_equals(dom.outerShadow.activeElement, dom.outerShadowChild); 276 }, 'focus() on host with delegatesFocus with another host with no delegatesFocus and a focusable child'); 277 278 test(() => { 279 const dom = createNestedHosts(true); 280 dom.outerHost.focus(); 281 assert_equals(document.activeElement, dom.outerHost); 282 assert_equals(dom.outerShadow.activeElement, dom.innerHost); 283 assert_equals(dom.innerShadow.activeElement, dom.innerShadowChild); 284 }, 'focus() on host with delegatesFocus with another host with delegatesFocus and a focusable child'); 285 286 test(() => { 287 // Structure: 288 // <div> host 289 // #shadowRoot root delegatesFocus=true 290 // <slot> 291 // (slotted) <div> 292 // <input> 293 // <input #firstFocusable> 294 const host = document.createElement("div"); 295 const slotted = document.createElement("div"); 296 slotted.appendChild(document.createElement("input")); 297 host.appendChild(slotted); 298 299 const root = host.attachShadow({mode: "open", delegatesFocus: true}); 300 301 const firstFocusable = document.createElement("input"); 302 root.innerHTML = "<slot>"; 303 root.appendChild(firstFocusable); 304 305 document.body.appendChild(host); 306 307 host.focus(); 308 assert_equals(document.activeElement, host); 309 assert_equals(root.activeElement, firstFocusable); 310 }, "focus() on host with delegatesFocus and slotted focusable children"); 311 </script>