如何在用户键入时正确处理内联错误消息(ARIA)?

发布于 2025-01-16 21:05:15 字数 332 浏览 4 评论 0 原文

我们的网站上有一个表单,其中包含在用户输入时使用 JavaScript 进行验证的字段(onChange 事件)。错误消息以红色文本显示在字段输入下方。

目前,我们使用 aria-descriptedby 和 aria-invalid 属性将消息呈现给 NVDA。屏幕阅读器会正确地播报字段何时无效,但不会播报字段何时再次有效。我们的测试团队强调这是有问题的。

我们还研究了将 role="alert" 添加到错误字段,但这似乎会在该字段无效时导致“双声” - 它会因为 role="alert" 而宣布错误一次,并再次因为咏叹调描述。

处理这个问题的正确/推荐方法是什么?

We have a form on our website containing fields that are validated with JavaScript as the user types (onChange event). The error messages are shown below the field inputs in red text.

We currently use the aria-describedby and aria-invalid attributes to surface the message to NVDA. The screen-reader correctly announces when the field becomes invalid, however it doesn't announce when the field becomes valid again. This was highlighted as problematic by our testing team.

We also looked into adding role="alert" to the error field, but this appears to cause "double-speak" when the field becomes invalid - it announces the error once because of the role="alert", and again because of the aria-describedby.

What is the correct/recommended way to handle this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

執念 2025-01-23 21:05:15

你已经很接近了。对于错误消息,您基本上需要做两件事,以使每个人都可以访问它们(WCAG 3.3.1)。

  1. 发生错误时宣布错误消息
  2. 将错误消息与错误字段相关联

听起来您正在通过 aria-描述者aria-invalid。如果用户导航离开错误的字段然后返回,他们会听到该字段的标签(希望您通过 拥有与该字段关联的标签) 并且他们会听到与其关联的消息作为字段的描述,因为aria-descriptedby。他们还会听到由于 aria-invalid 而将该字段宣布为“无效”

对于#1,您需要使用实时区域。使用 role="alert" 是一种方法获取实时区域,但这会导致隐式 aria-live="自信”。通常不需要断言的活动区域,除非消息非常重要以至于屏幕阅读器需要停止朗读它所说的内容来宣布您的消息。通常最好使用 aria-live="polite"

在您的情况下,如果在用户键入时显示错误(例如,如果您有一个“创建密码”字段,并且您想在键入 [大写、小写、数字、特殊字符等时检查条件) ]),然后使用 aria-live="polite" 将允许用户以正常速度键入并听到他们正在键入的字符,当他们暂停一秒钟时然后您的消息将被公布。

如果您使用 aria-live="assertive" (或 role="alert"),那么当用户键入时,如果您显示错误消息,则字符用户正在输入的内容将不再被宣布,因为屏幕阅读器将停止说出这些字符,而是会宣布您的错误消息。断言消息可能会清除屏幕阅读器将要显示的任何未决公告,因此用户在宣布错误时可能听不到他们键入的任何其他字符,因此现在他们不知道自己键入了什么。

来自 aria-live 规范

当发生断言更改时,用户代理或辅助技术可以选择清除排队的更改。

无论如何,综上所述,您的代码应该如下所示:

<label for="myid">Enter stuff</label>
<input id="myid" aria-describedby="myerror">
<div aria-live="polite" id="myerror"></div>

当用户键入并且 onChange() 运行时,当您发现错误时,您将设置 aria-invalid设置为 true 并将错误消息文本注入到

元素中,然后就会公布。

您问题的第二部分是用户何时修复该字段。用户如何知道错误何时被清除?当用户键入并且错误已更正时,您将 aria-invalid 设置为 false,并清除

。有时,当切换 ARIA 属性的值时,会自动宣布更改。这种情况发生在 aria-expandedaria-checked 中,但通常不会发生在 aria-invalid 中(尽管它如果有的话可能会很好)。

因此,如果您想告诉用户该字段不再出错,则需要一个明显隐藏的第二 aria-live 区域,该区域仅用于宣布“全部清除”消息。当您清除

中的错误消息时,由于活动区域宣布内容更改,您的内容从错误消息更改为空白文本,屏幕阅读器不会确实没有什么要宣布的。我想它可以说“空白”,因为这是新内容,但这并不能真正告诉用户它不再出错了。

因此,对于第二个活动区域,您可以在其中注入“未发现错误”或“错误已清除”或设计师认为合适的一些消息。所以现在的代码是:

<label for="myid">Enter stuff</label>
<input id="myid" aria-describedby="myerror">
<div aria-live="polite" id="myerror"></div>
<div aria-live="polite" id="allclear" class="sr-only"></div>

当确定要清除错误时,将文本注入

并将其公布。除了注入

并设置 之外,您还必须更新错误检测代码,以便在发现错误时清除“allclear”消息>aria-无效

最后一部分是“allclear”上使用的“sr-only”类。这不是一个预定义的类,而是一个类的通用名称,该类可以明显地隐藏某个元素,以便有视力的用户看不到该元素(您不想在屏幕上实际看到“全部清除”消息),但屏幕上会显示该元素。读者仍然可以访问文本。您可以在这里找到“sr-only”的示例定义,什么是 sr-仅在 Bootstrap 3 中?

我知道这看起来有很多信息,但是一旦您将其全部编码,那就没那么糟糕了。

最后附注,如果您的错误消息颜色是纯红色,#FF0000,那么在白色背景 (4:1) 上将没有足够的对比度。您需要稍深的红色,例如#ee0000 (4.5:1)。这将满足 WCAG 1.4.3

You're pretty close. There are essentially two things you need to do with error messages to make them accessible to everyone (WCAG 3.3.1).

  1. announce the error message when it happens
  2. associate the error message with the field in error

It sounds like you're handling #2 via aria-describedby and aria-invalid. If the user navigates away from the field in error then navigates back, they'll hear the field's label (hopefully you have the label associated with the field via <label for="id">) and they'll hear the message associated with it as the description of the field because of aria-describedby. They will also hear the field announced as "invalid" because of aria-invalid.

For #1 you'll want to use a live region. Using role="alert" is one way to get a live region but that causes an implicit aria-live="assertive". Assertive live regions are generally not needed unless the message is so important that the screen reader needs to stop announcing whatever it was saying to announce your message. It's generally better to use aria-live="polite".

In your case, if the error is displayed as the user is typing (for example, if you have a "create password" field and you want to check conditions off as they type [upper case, lower case, number, special char, etc]), then using aria-live="polite" will allow the user to type at their normal speed and hear the characters they're typing and when they pause for a second then your message will be announced.

If instead you use aria-live="assertive" (or role="alert"), then as the user is typing, if you display the error message, the characters that the user is typing will no longer be announced because the screen reader will stop saying the characters and will instead announce your error message. Assertive messages might clear any pending announcement the screen reader was going to say so the user might not hear any other characters they typed when the error was announced so now they won't know what they typed.

From aria-live spec:

User agents or assistive technologies MAY choose to clear queued changes when an assertive change occurs.

Anyway, with all that being said, you're code should look something like this:

<label for="myid">Enter stuff</label>
<input id="myid" aria-describedby="myerror">
<div aria-live="polite" id="myerror"></div>

As the user is typing and your onChange() runs, when you discover an error, you'll set aria-invalid to true and inject the error message text into the <div id="myerror"> element and it'll be announced.

The second part of your question is when the user fixes the field. How will the user know when the error is cleared? As the user is typing and the error is corrected, you'll set aria-invalid to false and you'll clear the <div>. Sometimes when toggling the value of an ARIA attribute, the change is announced automatically. This happens for aria-expanded and aria-checked but generally does not happen for aria-invalid (although it might be nice if it did).

So if you want to tell the user that the field is not in error anymore, you'd need a second aria-live region that is visibly hidden that is used solely for announcing the "all clear" message. When you clear the error message from your <div id="myerror">, because live regions announce the change in content, your content changed from an error message to blank text, the screen reader doesn't really have anything to announce. I suppose it could say "blank" because that's the new content but that doesn't really tell the user that it's not in error anymore.

So with a second live region, you can inject it with a "no errors found" or "errors cleared" or some message your designer deems appropriate. So the code would now be:

<label for="myid">Enter stuff</label>
<input id="myid" aria-describedby="myerror">
<div aria-live="polite" id="myerror"></div>
<div aria-live="polite" id="allclear" class="sr-only"></div>

When an error is determined to be cleared, you inject the text into the <div id="allclear"> and it'll be announced. You'll also have to update your error detection code to clear the "allclear" message when an error is found, in addition to injecting into <div id="myerror"> and setting aria-invalid.

The last part is the "sr-only" class used on the "allclear". That's not a predefined class but is a common name for a class that can visibly hide an element so that sighted users don't see the element (you don't want to physically see the "all clear" message on the screen) but screen readers still have access to the text. You can find a sample definition of "sr-only" here, What is sr-only in Bootstrap 3?

I know that looks like a lot of information but once you have it all coded, it's not that bad.

As a final side note, if your error message color is pure red, #FF0000, then that won't have sufficient contrast on a white background (4:1). You'll need a slightly darker red such as #ee0000 (4.5:1). This will satisfy WCAG 1.4.3

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文