如何允许屏幕阅读器在错误地将焦点移动到字段时宣布 ASP.NET MVC 5 表单验证消息?

How to allow screen readers to announce ASP.NET MVC 5 form validation messages when moving focus to the field in error?

我们有一个 ASP.NET MVC 5 网络应用程序。我们使用 JAWS 结合 Internet Explorer 11(企业授权)和 Chrome 定期进行可访问性测试。当用户 TAB 进入字段时,我们将 运行 解决 JAWS 未读取与表单字段关联的验证消息的问题。我们使用 FluentValidation 和标准 HTML 助手来显示表单字段和验证消息(下面的示例):

@Html.LabelFor(model => model.Email)
@Html.EditorFor(model => model.Email)
@Html.ValidationMessageFor(model => model.Email, null, new { role = "alert" })

示例 FluentValidation 可能会在数据库中查询表单中的电子邮件地址,并显示 "This e-mail has already been taken" 在服务器端运行的消息。

发送回浏览器的结果HTML是:

<label for="Email">E-mail address:</label>

<input type="text" name="Email" id="Email" ...>

<span class="..." data-valmsg-for="Email" data-valmsg-replace="true" role="alert">
    This e-mail has already been taken
</span>

没有任何内容将验证消息与表单字段相关联。我一直认为 MVC 框架会自动建立这个连接,但显然它不会。

根据 WebAIM,我们应该利用 aria-describedby 属性将表单字段与内联验证错误相关联,但是重新部署现有的 MVC5 框架来做到这一点是一项艰巨的任务。

如何在不重写主要 HTML 帮助程序的情况下让屏幕阅读器在将焦点置于由 ASP.NET MVC5 生成的表单字段时宣布内联验证消息?

从纯粹的 html 角度来看,aria-describedby 属性就是您处理该问题的方式。

<label for="Email">E-mail address:</label>
<input type="text" id="Email" aria-describedby="more_stuff">
<span id="more_stuff">
    This e-mail has already been taken
</span>

我不确定为什么这需要 "replumbing" 框架或编写 "major" 助手,但我不是 asp.net 用户。如果框架不允许您将描述与字段相关联,则说明框架存在缺陷,应向框架提交功能请求。

如果不创建自定义 HtmlHelper 扩展方法来在 for 控件中生成 aria-describedby 属性,并在错误元素中生成关联的 id 属性,您将需要使用 javascript 添加它们。

请注意,每个错误消息占位符(由 @Html.ValidationMessageFor() 生成)都通过 data-valmsg-for="...." 属性与其表单控件相关联。

假设您想要为所有表单控件包含 aria-describedby 以及关联的错误消息(并且在 <form> 元素中),以便在通过 [= 添加客户端错误时它可用18=],那么脚本 (jQuery) 将是

$(function () {
    // Get the form controls
    var controls = $('form').find('input, textarea, select')
        .not(':hidden, :input[type=submit], :input[type=button], :input[type=reset]');
    $.each(controls, function (index, item) {
        // Get the name of the form control
        var name = $(this).attr('name'); 
        if (!name) {
            return true;
        }
        // Get the associated error element
        var errorElement = $('[data-valmsg-for="' + name + '"]');
        if (!errorElement) {
            return true;
        }
        // Generate an id attribute based on the name of the control
        var errorId = name + "-error"
        // Add attributes to the input and the error element
        $(this).attr('aria-describedby', errorId)
        errorElement.attr('id', errorId);
    });
});

如果您对客户端验证错误不感兴趣,那么您可以使用 var controls = $('.input-validation-error'); 作为选择器来仅获取添加了服务器端验证错误的表单控件。

我建议将此脚本包含在外部(例如)screenreadervalidation.js 文件中,并将其包含在您的 jqueryjqueryval 包中,以便它包含在包含表单的所有视图中用于创建或编辑数据。

我通过 data-val-* 属性使用不显眼的验证,并创建包含验证错误消息的跨度,以便我可以控制放置。这样一来,我还可以轻松地为他们分配一个 ID 并使用 aria-describedby。当发生错误并在客户关注输入时填充 span 时,reader 会找到相关的 span ID 并读取出现在那里的错误。这里的关键部分是 aria-describedby="NameOfCustomerError" 匹配 id="NameOfCustomerError" 将出现错误的位置(因为它是带有 data-valmsg-for="NameOfCustomer" 的元素,表示它会自动显示该输入的错误)​​。

<input data-val="true"
   data-val-required="@($"{CustomerResource.Label_Name}: {SharedResource.Validation_General}")"
   data-val-minlength-min="4"
   data-val-minlength="@CustomerResource.Validation_NameOfCustomer"
   type="text" maxlength="100"
   name="NameOfCustomer" id="NameOfCustomer"
   class="form-control" aria-describedby="NameOfCustomerError"
   placeholder="@CustomerResource.Label_Name" />

<span role="alert" id="NameOfCustomerError" class="field-validation-valid text-danger" data-valmsg-for="NameOfCustomer" data-valmsg-replace="true"></span>