Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

<!DOCTYPE html>
<meta charset="utf-8">
<title>HTML Test: focus() on shadow host with delegatesFocus</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="resources/shadow-utils.js"></script>
<body>
<div id="host">
<div id="slottedToSecondSlot" slot="secondSlot">slottedToSecondSlot</div>
<div id="slottedToFirstSlot" slot="firstSlot">slottedToFirstSlot</div>
</div>
<div id="outside">outside</div>
</body>
<script>
const host = document.getElementById("host");
const slottedToSecondSlot = document.getElementById("slottedToSecondSlot");
const slottedToFirstSlot = document.getElementById("slottedToFirstSlot");
const outside = document.getElementById("outside");
const shadowRoot = host.attachShadow({ mode: "open", delegatesFocus: true });
const aboveSlots = document.createElement("div");
aboveSlots.innerText = "aboveSlots";
const firstSlot = document.createElement("slot");
firstSlot.name = "firstSlot";
const secondSlot = document.createElement("slot");
secondSlot.name = "secondSlot";
const belowSlots = document.createElement("div");
belowSlots.innerText = "belowSlots";
shadowRoot.appendChild(aboveSlots);
shadowRoot.appendChild(firstSlot);
shadowRoot.appendChild(secondSlot);
shadowRoot.appendChild(belowSlots);
const elementsInFlatTreeOrder = [host, aboveSlots, firstSlot,
slottedToFirstSlot, secondSlot, slottedToSecondSlot, belowSlots, outside];
// Final structure:
// <div #host> (delegatesFocus=true)
// #shadowRoot
// <div #aboveSlots>
// <slot #firstSlot>
// (slotted) <div #slottedToFirstSlot>
// <slot #secondSlot>
// (slotted) <div #slottedToSecondSlot>
// <div #belowSlots>
// <div #outside>
function setAllTabIndex(value) {
setTabIndex(elementsInFlatTreeOrder, value);
}
function removeAllTabIndex() {
removeTabIndex(elementsInFlatTreeOrder);
}
function resetTabIndexAndFocus() {
removeAllTabIndex();
resetFocus(document);
resetFocus(shadowRoot);
}
test(() => {
resetTabIndexAndFocus();
setAllTabIndex(0);
// Structure:
// <div #host> (delegatesFocus=true) tabindex=0
// #shadowRoot
// <div #aboveSlots> tabindex=0
// <slot #firstSlot> tabindex=0
// (slotted) <div #slottedToFirstSlot> tabindex=0
// <slot #secondSlot> tabindex=0
// (slotted) <div #slottedToSecondSlot> tabindex=0
// <div #belowSlots> tabindex=0
// <div #outside> tabindex=0
// First focusable = #aboveSlots
host.focus();
assert_equals(shadowRoot.activeElement, aboveSlots);
assert_equals(document.activeElement, host);
}, "focus() on host with delegatesFocus, all tabindex=0");
test(() => {
resetTabIndexAndFocus();
setAllTabIndex(0);
setTabIndex([host], -1);
// First focusable = #aboveSlots
host.focus();
assert_equals(shadowRoot.activeElement, aboveSlots);
assert_equals(document.activeElement, host);
}, "focus() on host with delegatesFocus & tabindex =-1, all other tabindex=0");
test(() => {
resetTabIndexAndFocus();
setTabIndex([aboveSlots, slottedToFirstSlot, slottedToSecondSlot, belowSlots], 0);
// First focusable = #aboveSlots
host.focus();
assert_equals(shadowRoot.activeElement, aboveSlots);
assert_equals(document.activeElement, host);
}, "focus() on host with delegatesFocus & no tabindex, all other tabindex=0");
test(() => {
resetTabIndexAndFocus();
setAllTabIndex(-1);
setTabIndex([host], 0);
// First focusable = #aboveSlots
host.focus();
assert_equals(shadowRoot.activeElement, aboveSlots);
assert_equals(document.activeElement, host);
}, "focus() on host with delegatesFocus & tabindex = 0, all other tabindex=-1");
test(() => {
resetTabIndexAndFocus();
removeAllTabIndex();
// No focusable element under #host in the flat tree.
host.focus();
assert_equals(shadowRoot.activeElement, null);
assert_equals(document.activeElement, document.body);
}, "focus() on host with delegatesFocus, all without tabindex");
test(() => {
resetTabIndexAndFocus();
// First focusable = #aboveSlots
setAllTabIndex(-1);
host.focus();
assert_equals(shadowRoot.activeElement, aboveSlots);
assert_equals(document.activeElement, host);
}, "focus() on host with delegatesFocus, all tabindex=-1");
test(() => {
resetTabIndexAndFocus();
removeAllTabIndex();
setTabIndex([host, belowSlots], 0);
// Structure:
// <div #host> (delegatesFocus=true) tabindex=0
// #shadowRoot
// <div #aboveSlots>
// <slot #firstSlot>
// (slotted) <div #slottedToFirstSlot>
// <slot #secondSlot>
// (slotted) <div #slottedToSecondSlot>
// <div #belowSlots> tabindex=0
// <div #outside>
// First focusable = #belowSlots
host.focus();
assert_equals(shadowRoot.activeElement, belowSlots);
assert_equals(document.activeElement, host);
}, "focus() on host with delegatesFocus & tabindex=0, #belowSlots with tabindex=0");
test(() => {
resetTabIndexAndFocus();
removeAllTabIndex();
setTabIndex([host, outside], 0);
// Structure:
// <div #host> (delegatesFocus=true) tabindex=0
// #shadowRoot
// <div #aboveSlots>
// <slot #firstSlot>
// (slotted) <div #slottedToFirstSlot>
// <slot #secondSlot>
// (slotted) <div #slottedToSecondSlot>
// <div #belowSlots>
// <div #outside> tabindex=0
// No focusable element under #host in the flat tree.
host.focus();
assert_equals(shadowRoot.activeElement, null);
assert_equals(document.activeElement, document.body);
}, "focus() on host with delegatesFocus & tabindex=0, #outside with tabindex=0");
test(() => {
resetTabIndexAndFocus();
setTabIndex([host, aboveSlots, belowSlots], 0);
// Structure:
// <div #host> (delegatesFocus=true) tabindex=0
// #shadowRoot
// <div #aboveSlots> tabindex=0
// <slot #firstSlot>
// (slotted) <div #slottedToFirstSlot>
// <slot #secondSlot>
// (slotted) <div #slottedToSecondSlot>
// <div #belowSlots> tabindex=0
// <div #outside>
// First focusable = #aboveSlots
host.focus();
assert_equals(shadowRoot.activeElement, aboveSlots);
assert_equals(document.activeElement, host);
}, "focus() on host with delegatesFocus & tabindex=0, #aboveSlots and #belowSlots with tabindex=0");
test(() => {
resetTabIndexAndFocus();
setTabIndex([host, aboveSlots], 0);
setTabIndex([belowSlots], 1);
// Structure:
// <div #host> (delegatesFocus=true) tabindex=0
// #shadowRoot
// <div #aboveSlots> tabindex=0
// <slot #firstSlot>
// (slotted) <div #slottedToFirstSlot>
// <slot #secondSlot>
// (slotted) <div #slottedToSecondSlot>
// <div #belowSlots> tabindex=1
// <div #outside>
// First focusable = #aboveSlots
host.focus();
assert_equals(shadowRoot.activeElement, aboveSlots);
assert_equals(document.activeElement, host);
}, "focus() on host with delegatesFocus & tabindex=0, #aboveSlots with tabindex=0 and #belowSlots with tabindex=1");
test(() => {
resetTabIndexAndFocus();
setTabIndex([host, slottedToFirstSlot, slottedToSecondSlot, belowSlots], 0);
// Structure:
// <div #host> (delegatesFocus=true) tabindex=0
// #shadowRoot
// <div #aboveSlots>
// <slot #firstSlot>
// (slotted) <div #slottedToFirstSlot> tabindex=0
// <slot #secondSlot>
// (slotted) <div #slottedToSecondSlot> tabindex=0
// <div #belowSlots> tabindex=0
// <div #outside>
// First focusable = #slottedToFirstSlot
host.focus();
assert_equals(shadowRoot.activeElement, belowSlots);
assert_equals(document.activeElement, host);
}, "focus() on host with delegatesFocus & tabindex=0, #slottedToFirstSlot, #slottedToSecondSlot, #belowSlots with tabindex=0");
test(() => {
resetTabIndexAndFocus();
setTabIndex([aboveSlots, belowSlots], 0);
belowSlots.focus();
host.focus();
assert_equals(shadowRoot.activeElement, belowSlots);
}, "focus() on host with delegatesFocus and already-focused non-first shadow descendant");
function createNestedHosts(innerDelegatesFocus) {
// Structure:
// <div> outerHost
// <input> outerLightChild
// #shadowRoot outerShadow delegatesFocus=true
// <span> innerHost
// #shadowRoot inneShadow delegatesFocus=true/false
// <input> innerShadowChild
// <input> outerShadowChild
const outerHost = document.createElement('div');
const outerLightChild = document.createElement('input');
outerHost.appendChild(outerLightChild);
const innerHost = document.createElement('span');
const outerShadow = outerHost.attachShadow({mode: 'closed', delegatesFocus:true});
outerShadow.appendChild(innerHost);
const outerShadowChild = document.createElement('input');
outerShadow.appendChild(outerShadowChild);
const innerShadow = innerHost.attachShadow({mode: 'closed', delegatesFocus:innerDelegatesFocus});
const innerShadowChild = document.createElement('input');
innerShadow.appendChild(innerShadowChild);
document.body.insertBefore(outerHost, document.body.firstChild);
return {outerHost: outerHost,
outerLightChild: outerLightChild,
outerShadow: outerShadow,
outerShadowChild: outerShadowChild,
innerHost: innerHost,
innerShadow: innerShadow,
innerShadowChild: innerShadowChild};
}
test(() => {
const dom = createNestedHosts(false);
dom.outerHost.focus();
assert_equals(document.activeElement, dom.outerHost);
assert_equals(dom.outerShadow.activeElement, dom.outerShadowChild);
}, 'focus() on host with delegatesFocus with another host with no delegatesFocus and a focusable child');
test(() => {
const dom = createNestedHosts(true);
dom.outerHost.focus();
assert_equals(document.activeElement, dom.outerHost);
assert_equals(dom.outerShadow.activeElement, dom.innerHost);
assert_equals(dom.innerShadow.activeElement, dom.innerShadowChild);
}, 'focus() on host with delegatesFocus with another host with delegatesFocus and a focusable child');
test(() => {
// Structure:
// <div> host
// #shadowRoot root delegatesFocus=true
// <slot>
// (slotted) <div>
// <input>
// <input #firstFocusable>
const host = document.createElement("div");
const slotted = document.createElement("div");
slotted.appendChild(document.createElement("input"));
host.appendChild(slotted);
const root = host.attachShadow({mode: "open", delegatesFocus: true});
const firstFocusable = document.createElement("input");
root.innerHTML = "<slot>";
root.appendChild(firstFocusable);
document.body.appendChild(host);
host.focus();
assert_equals(document.activeElement, host);
assert_equals(root.activeElement, firstFocusable);
}, "focus() on host with delegatesFocus and slotted focusable children");
</script>