让屏幕阅读器阅读使用 JavaScript 添加的新内容

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3670570/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 05:39:26  来源:igfitidea点击:

Getting screen reader to read new content added with JavaScript

javascriptaccessibilityscreen-readers

提问by avernet

When a web page is loaded, screen readers (like the one that comes with OS X, or JAWS on Windows) will read the content of the whole page. But say your page is dynamic, and as users performing an action, new content gets added to the page. For the sake of simplicity, say you display a message somewhere in a <span>. How can you get the screen reader to read that new message?

加载网页时,屏幕阅读器(如 OS X 或 Windows 上的 JAWS 附带的阅读器)将读取整个页面的内容。但是假设您的页面是动态的,并且随着用户执行操作,新内容会添加到页面中。为简单起见,假设您在<span>. 如何让屏幕阅读器阅读新消息?

回答by Courtney Christensen

The WAI-ARIAspecification defines several ways by which screen readers can "watch" a DOM element. The best supported method is the aria-liveattribute. It has modes off, polite,assertiveand rude. The higher the level of assertiveness, the more likely it is to interrupt what is currently being spoken by the screen reader.

WAI-ARIA规范定义了几种方法,使屏幕阅读器可以“看” DOM元素。最受支持的方法是aria-live属性。它有模式offpoliteassertiverude。自信程度越高,就越有可能打断屏幕阅读器当前所说的内容。

The following has been tested with NVDAunder Firefox 3 and Firefox 4.0b9:

以下已在 Firefox 3 和 Firefox 4.0b9 下使用NVDA进行了测试:

<!DOCTYPE html>
<html>
<head>
  <script src="js/jquery-1.4.2.min.js"></script>
</head>
<body>
  <button onclick="$('#statusbar').html(new Date().toString())">Update</button>
  <div id="statusbar" aria-live="assertive"></div>
</body>

The same thing can be accomplishedwith WAI-ARIA rolesrole="status"and role="alert". I have had reports of incompatibility, but have not been able to reproduce them.

同样的事情可以完成与WAI-ARIA角色role="status"role="alert"。我收到了不兼容的报告,但无法重现它们。

<div id="statusbar" role="status">...</div>

回答by Marcy Sutton

Here is an adapted real world example -- this up-level markup has already been converted from an unordered list with links into a select menu via JS. The real code is a lot more complex and obviously could not be included in its entirety, so remember this will have to be rethought for production use. For the select menu to be made keyboard accessible, we registered the keypress & onchange events and fired the AJAX call when users tabbed off of the list (beware of browser differences in timing of the onchange event). This was a serious PITA to make accessible, but it IS possible.

这是一个适应现实世界的例子——这个上级标记已经通过 JS 从一个带有链接的无序列表转换为一个选择菜单。真正的代码要复杂得多,显然不能全部包含在内,所以请记住,在生产使用时必须重新考虑这一点。为了使选择菜单可通过键盘访问,我们注册了 keypress 和 onchange 事件,并在用户从列表中退出时触发了 AJAX 调用(注意浏览器 onchange 事件时间的差异)。这是一个严肃的 PITA,可以访问,但它是可能的。

  //  HTML

  <!-- select element with content URL -->
  <label for="select_element">State</label>
  <select id="select_element">
     <option value="#URL_TO_CONTENT_PAGE#" rel="alabama">Alabama</option>
  </select>
  <p id="loading_element">Content Loading</p>

  <!-- AJAX content loads into this container -->
  <div id="results_container"></div>


  // JAVASCRIPT (abstracted from a Prototype class, DO NOT use as-is)

  var selectMenu = $('select_element');
  var loadingElement = $('loading_element');
  var resultsContainer = $('results_container');

 // listen for keypress event (omitted other listeners and support test logic)
  this.selectMenu.addEventListener('keypress', this.__keyPressDetector, false);


 /* event callbacks */

 // Keypress listener

  __keyPressDetector:function(e){

    // if we are arrowing through the select, enable the loading element
    if(e.keyCode === 40 || e.keyCode === 38){
        if(e.target.id === 'select_element'){
            this.loadingElement.setAttribute('tabIndex','0');
        }
    }
    // if we tab off of the select, send focus to the loading element
    //  while it is fetching data
     else if(e.keyCode === 9){
        if(targ.id === 'select_element' && targ.options[targ.selectedIndex].value !== ''){            
            this.__changeStateDetector(e);

            this.loadingElement.focus();

        }   
    }
}

// content changer (also used for clicks)
__changeStateDetector:function(e){

    // only execute if there is a state change
    if(this.selectedState !== e.target.options[e.target.selectedIndex].rel){

       // get state name and file path
       var stateName = e.target.options[e.target.selectedIndex].rel;
       var stateFile = e.target.options[e.target.selectedIndex].value;

       // get the state file
       this.getStateFile(stateFile);

       this.selectedState = stateName;

    }
}

getStateFile:function(stateFile){
    new Ajax.Request(stateFile, {
        method: 'get',
        onSuccess:function(transport){      

            // insert markup into container
            var markup = transport.responseText;

            // NOTE: select which part of the fetched page you want to insert, 
            // this code was written to grab the whole page and sort later

            this.resultsContainer.update(markup);

            var timeout = setTimeout(function(){

                // focus on new content
               this.resultsContainer.focus();

            }.bind(this), 150);

        }.bind(this)
    });
}