Course outline

JavaScript / Advanced: scheduling and trustworthy interfaces / Lesson 16

Accessible JavaScript interactions

Preserve native semantics while updating state dynamically. Connect labels, keyboard behavior, focus, errors, and live feedback to complete user tasks rather than a passing automated scan.

About 4 min readContent reviewed
On this page

Lesson goals

  • Choose native controls and meaningful names.
  • Coordinate focus and status during state changes.
  • Test keyboard and assistive-technology task completion.

Behavior starts with the right element

A button performs an action and a link navigates to a resource. Native elements already provide important keyboard and accessibility semantics; rebuilding them with a clickable div creates additional obligations. JavaScript should enhance that foundation rather than suppressing default behavior reflexively. Give controls a visible, meaningful name and ensure any accessible-name override includes the visible label so speech-input users can refer to it. A disabled state needs a reason and a recovery path where appropriate. Do not make an unavailable control the only place explaining what the user must do next.

Announce a change without stealing focus

An enrollment count can update in a status region while focus remains on the button that initiated it. Set the region up before changing its text and choose a concise message meaningful in context. Live regions help expose updates but do not guarantee identical announcements across every assistive-technology combination. The example constructs a detached component and verifies its text result; it does not claim a screen reader announced it. Real evaluation must exercise the mounted component using supported technologies and ensure repeated actions do not create noisy duplicate messages.

javascript Browser-only: native action and a status message
const root = document.createElement("section");
const button = document.createElement("button");
button.type = "button"; button.textContent = "Enroll";
const status = document.createElement("p");
status.setAttribute("role", "status");
button.addEventListener("click", () => { status.textContent = "1 enrolled"; });
root.append(button, status);
button.click();
console.log(status.textContent);
console.log(button.tagName, status.getAttribute("role"));
Expected output
1 enrolled
BUTTON status

The check establishes DOM state only. Keyboard operation, focus, and actual announcement need a mounted user-task evaluation.

Focus follows the task lifecycle

When a dialog opens, users need a meaningful focus destination, usable controls, a supported dismissal path, and a sensible focus return when it closes. Prefer the native dialog element where its behavior fits, then verify the complete interaction rather than assuming a role attribute implements it. Do not move focus for every status update or rerender the focused control unnecessarily. A removed focused node can strand a keyboard user. Preserve stable identities or choose a deliberate next focus target after deletion. Test opening, errors, correction, closing, and repeated opening as one connected task.

Associate errors with the field and preserve work

Validation should identify the field, explain the problem in text, and make correction possible without discarding unrelated valid entries. aria-invalid marks the invalid state; an associated description can provide the explanation. Color alone is insufficient, and an alert should not repeatedly announce unchanged errors on every keystroke. The example demonstrates a browser DOM association and its stored attributes. It does not replace native form constraints or server validation, and the exact moment of announcing an error should follow the task rather than a blanket rule that every character requires immediate criticism.

javascript Browser-only: label and error associations
const field = document.createElement("input");
field.id = "email"; field.type = "email";
const label = document.createElement("label");
label.htmlFor = field.id; label.textContent = "Email";
const error = document.createElement("p");
error.id = "email-error"; error.textContent = "Enter a valid email address.";
field.setAttribute("aria-invalid", "true");
field.setAttribute("aria-describedby", error.id);
console.log(label.htmlFor, field.getAttribute("aria-invalid"));
console.log(field.getAttribute("aria-describedby"));
Expected output
email true
email-error

The field has an explicit label target and description reference. A real mounted form must also verify those referenced nodes and correction behavior.

Evaluate complete processes and limitations

WCAG provides testable success criteria, while manual task evaluation supplies context that an automated tool cannot infer. Combine keyboard-only use, supported screen readers, enlargement/reflow, contrast checks, and automated structural checks. Inspect dynamic states such as loading, failure, and empty results, not only the initial page. Record tested pages, process steps, technologies, and unresolved issues. A zero-error scan does not establish conformance or meet every user need. Include people with disabilities where feasible and use their findings to improve the actual task, without assuming one participant represents all experiences.

Common errors and fixes

A custom div acts like an incomplete button

Why it happens
Click handling replaces native keyboard semantics.
How to fix it
Use a native button with the intended type and label.
Prevent it next time
Test the same task with keyboard only.

Every update moves focus unexpectedly

Why it happens
Announcements and focus management are conflated.
How to fix it
Use an appropriate status region and move focus only when the task needs it.
Prevent it next time
Review focus before and after each dynamic state change.

A clean scan is claimed as universal accessibility

Why it happens
Detectable structural rules are confused with complete task usability.
How to fix it
Add manual task and assistive-technology checks with explicit scope.
Prevent it next time
Report criteria, methods, and unresolved states.

Check your understanding

1. A save button says Save but aria-label says Action. What is wrong?

Show worked solution

The accessible name excludes visible text, making speech-input targeting unreliable. Use visible content as the name or an override that includes Save, such as Save draft. Verify the final computed name and keyboard behavior rather than checking attributes alone.

2. What should a failed form submission preserve and communicate?

Show worked solution

Preserve valid entries, identify invalid fields with associated textual guidance, and provide a logical correction path. Choose focus movement for the task and announce summary/status where useful. Retest correction and successful resubmission, not merely the initial red styling.

Key takeaways

  • Native elements and stable focus reduce avoidable interaction complexity.
  • Accessibility evidence includes dynamic task completion and explicit limits.

Official references