Webページが読み込まれると、スクリーンリーダー(OS XやWindowsのJAWSに付属するものなど)がページ全体のコンテンツを読み上げます。しかし、ページが動的で、ユーザーがアクションを実行すると、新しいコンテンツがページに追加されます。簡単にするために、<span>
のどこかにメッセージを表示するとします。スクリーンリーダーで新しいメッセージを読み上げるにはどうすればよいですか。
WAI-ARIA 仕様では、スクリーンリーダーがDOM要素を「監視」できるいくつかの方法を定義しています。サポートされている最良の方法は aria-live
属性。モードはoff
、polite
、assertive
およびrude
です。断定性のレベルが高いほど、スクリーンリーダーが現在話している内容を妨害する可能性が高くなります。
以下は、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>
同じことができます 達成 WAI-ARIA ロールrole="status"
およびrole="alert"
。非互換性の報告はありましたが、再現できませんでした。
<div id="statusbar" role="status">...</div>
これが実際の世界に適応した例です。この上位レベルのマークアップは、JSを介してリンク付きの順序付けられていないリストから選択メニューにすでに変換されています。実際のコードははるかに複雑であり、明らかにその全体を含めることはできません。したがって、本番用に再検討する必要があることに注意してください。選択メニューをキーボードでアクセスできるようにするために、keypress&onchangeイベントを登録し、ユーザーがリストからタブで移動したときにAJAX呼び出しを起動しました(onchangeイベントのタイミングのブラウザーの違いに注意してください)。 。これはアクセシブルにするための真剣なPITAでしたが、ISが可能です。
// 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)
});
}