-
Notifications
You must be signed in to change notification settings - Fork 12
Description
When an assertive live region is updated in a blur handler (at the same time the next form field receives focus via Tab), JAWS 2025 announces the new focus (next field’s label/role) before it announces the live region message. In JAWS 2024 and NVDA, the assertive live region announcement occurs first, then the focus speech.
This change in ordering is not a WCAG failure (4.1.3 doesn’t mandate order), but it reduces predictability and clarity for users, and diverges from JAWS 2024 and other AT behavior—so it’s a UX regression that makes inline error feedback less clear during keyboard tabbing between required fields.
Steps to reproduce
-
Open the attached minimal page (code below) in Chrome 139.
-
With JAWS 2025 running, Tab into “First name”.
-
Press Tab to move to “Last name” (triggering blur on First name).
-
Observe the announcement order.
Repeat the test with JAWS 2024 and NVDA for comparison.
Actual vs Expected
Actual (JAWS 2025):
JAWS 2025 announces “Last name” first → then announces the assertive live region (“Error: First name is a required field”).
Expected (based on prior versions & other AT):
Assertive live region update interrupts and is announced before the next focus announcement (this is what JAWS 2024 and NVDA currently do on the same page).
While the ARIA spec allows ATs to choose how to queue/interrupt, the shift within JAWS from 2024 → 2025 changes UX expectations and breaks cross‑AT consistency for inline validation patterns.
Desired behavior
Maintain the JAWS 2024 behavior (and NVDA parity) where an assertive live region update that occurs during a blur→focus transition wins and is announced before the next focus speech, or otherwise provide a JAWS setting to allow authors/users to preserve prior ordering for assertive updates during focus transitions.
Sample code
`<!doctype html>
<script src=https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js
integrity="sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg=="
crossorigin="anonymous"></script>
<script src=https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js
integrity="sha512-iztkobsvnjKfAtTNdHkGVjAYTrrtlC7mGp/54c40wowO7LhURYl3gVzzcEqGl/qKXQltJ2HwMrdLcNUdo+N/RQ=="
crossorigin="anonymous"></script>
<link rel="stylesheet" href=https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css
integrity="sha512-6MXa8B6uaO18Hid6blRMetEIoPqHf7Ux1tnyIQdpt9qI5OACx7C+O3IVTr98vwGnlcg0LOLa02i9Y1HpVhlfiw=="
crossorigin="anonymous" />
<link rel="stylesheet" href=https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/css/fontawesome.min.css
integrity="sha512-shT5e46zNSD6lt4dlJHb+7LoUko9QZXTGlmWWx0qjI9UhQrElRb+Q5DM7SVte9G9ZNmovz2qIaV7IWv0xQkBkw=="
crossorigin="anonymous" />
<link rel="stylesheet" href=https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.2/css/all.min.css
integrity="sha512-HK5fgLBL+xu6dm/Ii3z4xhlSUyZgTT9tuc/hSrtw6uzJOvgRr2a9jyxxT1ely+B+xFAmJKVSTbpM/CuL7qxO8w=="
crossorigin="anonymous" />
<link rel="stylesheet" href="../CSS/semanticCss.css" />
<title>form validation: text injected</title>
<!-- main -->
<div class="col-xs-12">
<main tabindex="-1" id="mainDiv">
<h1>form validation : text injecting approach</h1>
<form>
<div class="col-xs-12">
<span class="requiredFieldSpan">* required field</span>
</div>
<div class="col-xs-12">
<div role="alert" id="alertDiv" class="error">
</div>
</div>
<div class="col-xs-12">
<label for="txtFname">
First name
<span class="stars">*</span>
</label>
<input aria-invalid="false" required id="txtFname" type="text" />
<div id="errorDivFName" aria-live="assertive" aria-atomic="true">
<p id="liveErrorAnnouncement" class="sr-only">
</p>
</div>
</div>
<div class="col-xs-12">
<label for="txtLname">
Last name
<span class="stars">*</span>
</label>
<input aria-invalid="false" required id="txtLname" type="text" />
</div>
</form>
</main>
</div>
</div>
<script>
$(document).ready(function () {
$('input[type="text"]').on('blur', function () {
$('#liveErrorAnnouncement').removeClass("sr-only").text("Error:First name is a required field");
$(this).attr("aria-describedby", "liveErrorAnnouncement").attr("aria-invalid", "true");
});
});
</script>