frontend-patterns/research/experiments/navigation-history/focus-policy.js
tegwick 26d7c4523b Measure keyboard continuation and navigation feedback
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a0cffe-206f-7323-89d3-b20ca4cf4bf9
2026-09-27 00:22:14 +02:00

32 lines
1.6 KiB
JavaScript

// Authored experimental navigation policy; raw variant installs only diagnostics.
window.probe=[];
const managed=new URL(location.href).searchParams.get('mode')==='managed';
const pending=new WeakMap();
const notice=message=>{document.querySelector('#feedback').textContent=message;};
function complete(moveFocus){
const heading=document.querySelector('#heading');
document.title=`${heading.textContent} — Catalog`;
notice(`Loaded ${heading.textContent}`);
if(moveFocus)heading.focus();
}
document.addEventListener('htmx:beforeRequest',e=>{
if(managed&&e.detail.target.id==='content')pending.set(e.detail.xhr,{source:e.detail.elt,ownsFocus:false});
});
document.addEventListener('htmx:beforeSwap',e=>{
const state=pending.get(e.detail.xhr);
if(state)state.ownsFocus=document.activeElement===state.source;
});
document.addEventListener('htmx:afterSwap',e=>{
const state=pending.get(e.detail.xhr);
if(state){complete(state.ownsFocus);pending.delete(e.detail.xhr);}
});
document.addEventListener('htmx:responseError',e=>{
if(managed&&pending.has(e.detail.xhr)){
notice('Navigation failed. Current article retained. Use Native recovery to continue.');
pending.delete(e.detail.xhr);
}
});
document.addEventListener('htmx:historyRestore',()=>{if(managed)complete(true);});
for(const name of ['htmx:afterSettle','htmx:historyRestore','htmx:responseError']){
document.addEventListener(name,()=>window.probe.push({name,focus:document.activeElement.id||document.activeElement.tagName,title:document.title,url:location.href,feedback:document.querySelector('#feedback').textContent}));
}