Apply the focusing steps' get-the-focusable-area mapping before rejecting a non-focusable target. This preserves documentElement.focus() by mapping the non-focusable document element to the Document viewport. Also map rendered navigable containers with content navigables to their active document, while leaving hidden containers unfocused. Preserve Window focus events for child document viewports reached through iframe focus, while still suppressing the top-level viewport surrogate events. Treat rendered object elements as focusable through their default non-null tabindex, even when they show fallback or image content instead of a child navigable. Keep the spec focus-chain common-tail handling intact for viewport focus. The Document object is only our surrogate for the viewport, so designate viewport focus from the new focus target without dispatching Window focus/focusin events for that top-level surrogate. Pass that viewport surrogate as the fallback target for fragment scrolling and NavigateEvent focus reset, so unfocusable body or fragment targets still clear stale element focus. Cover documentElement.focus() in both the activeElement and focus-chain tests, including a tabindex document element that remains focused as an element. Cover object focus with and without a child navigable, hidden object focus attempts, iframe focus events, hidden iframe focus, and blurring a focused iframe after it becomes hidden. Also cover viewport fallback for intercepted navigation focus reset and fragment scrolling to an unfocusable target.
47 lines
1.4 KiB
HTML
47 lines
1.4 KiB
HTML
<!DOCTYPE html>
|
|
<script src="../include.js"></script>
|
|
<style>
|
|
#fragment-target {
|
|
margin-top: 500px;
|
|
height: 50px;
|
|
margin-bottom: 1000px;
|
|
}
|
|
</style>
|
|
<input id="before">
|
|
<div id="navigation-target"></div>
|
|
<div id="fragment-target"></div>
|
|
<script>
|
|
function activeElementName() {
|
|
return document.activeElement.id || document.activeElement.localName;
|
|
}
|
|
|
|
asyncTest(async done => {
|
|
await new Promise(resolve => window.addEventListener("load", resolve, { once: true }));
|
|
await timeout(0);
|
|
|
|
const before = document.getElementById("before");
|
|
const fragmentTarget = document.getElementById("fragment-target");
|
|
|
|
navigation.addEventListener("navigate", event => {
|
|
event.intercept({
|
|
scroll: "manual",
|
|
});
|
|
}, { once: true });
|
|
|
|
before.focus();
|
|
navigation.navigate("#navigation-target", { history: "push" });
|
|
await timeout(0);
|
|
println(`after intercepted navigation focus reset: ${activeElementName()}`);
|
|
|
|
before.focus();
|
|
const scrolled = new Promise(resolve => {
|
|
document.addEventListener("scroll", () => resolve(), { once: true });
|
|
});
|
|
|
|
location.hash = fragmentTarget.id;
|
|
await scrolled;
|
|
println(`after fragment focus fallback: ${activeElementName()}`);
|
|
|
|
done();
|
|
});
|
|
</script>
|