Which focus-and-announcement strategy should an htmx search-results workflow use for screen reader users?
0 reputation · 22 May 2021, 14:23 UTC
0 reputation · 22 May 2021, 14:23 UTC
I'm designing a search filter that issues hx-get requests and swaps results into a container with hx-target, using hx-indicator for the loading state. The goal is for screen reader and keyboard users to perceive each update as reliably as sighted users do.
From the htmx documentation I can see the event lifecycle (htmx:beforeRequest, htmx:afterSwap) is hookable, but the library itself appears to ship no default focus management or announcement behavior after a swap — that decision is left entirely to the implementation. I'm unsure which combination is considered sound: moving focus into the swapped region, writing a status message into an aria-live="polite" region, setting aria-busy on the target while the request is in flight, or some mix of all three. Moving focus on every keystroke-driven filter seems disruptive, yet announcing without focus movement risks users never finding the new content.
Assume htmx 1.9.x; I will verify event names and swap options against the installed version.
Should focus move to the swapped container after htmx:afterSwap, or is a live-region announcement alone acceptable for repeated filter updates? Does aria-busy on the target actually get announced reliably across NVDA and VoiceOver? Is there any community-accepted pattern for handling hx-swap-oob updates accessibly?
31775 reputation · 23 May 2021, 02:19 UTC
aria-live="polite" region for announcements rather than moving focus.Moving focus every keystroke or filter update is highly disruptive for screen reader users, as it strips them from the search input they are currently typing in. Instead, a live region allows the user to receive status updates about the results without interrupting their input flow.
nTo implement this correctly in htmx 1.9.x, follow these steps:
aria-live="polite" and aria-atomic="true". This element should remain on the page throughout the session.htmx:afterSwap event to inject a text summary into this live region (e.g., "Found 12 results").aria-busy="true" to the results container during htmx:beforeRequest and remove it on htmx:afterSwap to signal that the content is in flux.// JavaScript to handle the live region announcement
document.body.addEventListener('htmx:afterSwap', function(evt) {
const resultCount = evt.target.querySelectorAll('.result-item').length;
const statusRegion = document.getElementById('search-status');
statusRegion.innerText = `Search updated. ${resultCount} results found.`;\n});
While aria-busy is technically valid for indicating a loading state, its announcement reliability is inconsistent across screen readers like NVDA and VoiceOver compared to direct text changes. Therefore, the text-based live region update is the only way to ensure the user knows the update has finished. Regarding hx-swap-oob, ensure the OOB element is either part of the live region or triggers a status update itself, otherwise, the screen reader may ignore the injected DOM changes entirely.
Diagnostic Question: Does your search trigger on every keystroke (debounced) or only on a specific action like pressing Enter? This determines if you should use "polite" or "assertive" announcements.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.