focus-selector-delegatesFocus.html (4031B)
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8" /> 5 <title>CSS Test (Selectors): :focus behavior with shadow hosts & delegatesFocus </title> 6 <link rel="author" title="Rakina Zata Amni" href="rakina@chromium.org" /> 7 <link rel="help" href="https://html.spec.whatwg.org/multipage/semantics-other.html#selector-focus" /> 8 <script src="/resources/testharness.js"></script> 9 <script src="/resources/testharnessreport.js"></script> 10 <script src="resources/shadow-utils.js"></script> 11 </head> 12 13 <body> 14 <input> 15 16 <script> 17 function createFocusableDiv() { 18 const div = document.createElement("div"); 19 div.innerText = "foo"; 20 div.tabIndex = 0; 21 return div; 22 } 23 24 function createShadowHost(delegatesFocus, container) { 25 const host = document.createElement("div"); 26 host.attachShadow({ mode: "open", delegatesFocus: delegatesFocus }); 27 container.appendChild(host); 28 return host; 29 } 30 31 const delegatesFocusValues = [true, false]; 32 33 for (const delegatesFocus of delegatesFocusValues) { 34 test(() => { 35 resetFocus(); 36 const host = createShadowHost(delegatesFocus, document.body); 37 const shadowChild = createFocusableDiv(); 38 host.shadowRoot.appendChild(shadowChild); 39 40 shadowChild.focus(); 41 assert_true(shadowChild.matches(":focus"), "element in shadow tree matches :focus"); 42 assert_true(host.matches(":focus"), "host matches :focus"); 43 }, `:focus applies to host with delegatesFocus=${delegatesFocus} when the shadow root's descendant has focus`); 44 45 test(() => { 46 resetFocus(); 47 const host = createShadowHost(delegatesFocus, document.body); 48 const slotted = createFocusableDiv(); 49 host.shadowRoot.appendChild(document.createElement("slot")); 50 host.appendChild(slotted); 51 52 slotted.focus(); 53 assert_true(slotted.matches(":focus"), "slotted element matches :focus"); 54 assert_false(host.matches(":focus"), "host matches :focus"); 55 }, `:focus does not apply to host with delegatesFocus=${delegatesFocus} when slotted element has focus`); 56 57 for (const nestedDelegatesFocus of delegatesFocusValues) { 58 test(() => { 59 resetFocus(); 60 const host = createShadowHost(delegatesFocus, document.body); 61 const nestedHost = createShadowHost(nestedDelegatesFocus, host.shadowRoot); 62 const nestedShadowChild = createFocusableDiv(); 63 nestedHost.shadowRoot.appendChild(nestedShadowChild); 64 nestedShadowChild.focus(); 65 assert_true(nestedShadowChild.matches(":focus"), "element in nested shadow tree matches :focus"); 66 assert_true(nestedHost.matches(":focus"), "host of nested shadow tree matches focus"); 67 assert_true(host.matches(":focus"), "topmost host matches focus"); 68 }, `:focus applies to host with delegatesFocus=${delegatesFocus} when an element in a nested shadow tree with delegatesFocus=${nestedDelegatesFocus} is focused`); 69 70 test(() => { 71 resetFocus(); 72 const host = createShadowHost(delegatesFocus, document.body); 73 const nestedHost = createShadowHost(nestedDelegatesFocus, host.shadowRoot); 74 const nestedShadowChild = createFocusableDiv(); 75 nestedHost.shadowRoot.appendChild(nestedShadowChild); 76 // All nested shadow hosts should has :focus applied 77 nestedShadowChild.focus(); 78 79 const elementOutsideOfShadowDOM = document.querySelector("input"); 80 // Move the focus to an element which is outside of the nested 81 // shadow DOM trees 82 elementOutsideOfShadowDOM.focus(); 83 84 assert_false(nestedShadowChild.matches(":focus"), "element in nested shadow tree doesn't matche :focus"); 85 assert_false(nestedHost.matches(":focus"), "host of nested shadow tree doesn't match focus"); 86 assert_false(host.matches(":focus"), "topmost host matches focus"); 87 assert_true(elementOutsideOfShadowDOM.matches(":focus"), "The element outside of shadow dom matches :focus"); 88 }, `:focus should be removed from hosts with delegatesFocus=${delegatesFocus} when none of the elements in a nested shadow tree with delegatesFocus=${nestedDelegatesFocus} is focused`); 89 } 90 } 91 </script> 92 </body>