JavaScriptコンポーネントのアクセシビリティを高め、ユーザーがWebサイトやWebアプリをより快適に使用できるようにするためのコツを紹介します。
以前の記事『Writing HTML with accessibility in mind(アクセシビリティに配慮したHTMLを書く)』で、どうしてWebアクセシビリティについて考えるようになったのか、また、どのように学んだのか説明しました。そして、マークアップを改善して、Webサイトのアクセシビリティを高めるためのコツを紹介しました。中には基礎的な内容も含まれていますが、どれも価値のあるものです。こうしたコツをすべてまとめると、フロントエンド開発において特に重要な2つの不文律ができあがります。すなわち、基礎を学ぶこと、プランニングとHTMLを書くことの両方に十分な時間をかけることです。きれいでセマンティックなマークアップは開発者とユーザーの両方にとって有益です。
幸い、HTMLだけでWebサイトを作る必要はなく、ほかにも使える言語はあります。しかし、言語が複雑であればあるほど間違いが起きる可能性は高くなります。JavaScriptはとても複雑になり得る言語です。コードが動くことだけに満足していると、マウスやタッチパッド以外の入力デバイス、たとえば、キーボードやスクリーンリーダーを使うユーザーのことをすぐに見落としてしまいます。本記事はWebアクセシビリティに関して、JavaScriptを書くときに留意すべき点やJavaScriptコンポーネントのアクセシビリティ向上に関するコツを紹介します。
JavaScriptを敵視する必要はない
本題に入る前に大切なことを1つ伝えておきます。アクセシビリティの高いWebサイトの作成とは、JavaScriptを使うかどうかを決めることではありません。アクセシビリティとは、できるだけ多くの人がコンテンツを利用できるようにすることです。古いブラウザーやコンピューターを使うユーザー、インターネット接続速度が遅いまたはセキュリティ制限が厳しい(例:JavaScriptが無効)環境にあるユーザーなどもその対象に含まれます。JavaScriptが動作しない、読み込みにとても長い時間がかかるといった条件での利用は理想的ではないかもしれません。それでもWebサイトが利用しやすく、使い勝手が良いのなら問題ありません。
JavaScriptを実行できるなら、むしろJavaScriptを活用したほうがアクセシビリティが向上します。Sara Soueidanはツールチップウィジェットを作った経験を、完全なアクセシビリティを持ったツールチップのヘルプを作ることは思った以上に難しいと『Building a fully-accessible help tooltip』で書いています。「JavaScript以外のソリューションはどれもユーザーエクスペリエンスに悪影響を与える大きな欠点があった」と語っており、JavaScriptがアクセシビリティにとって重要である理由を説明しています。
Marco Zeheは『JavaScript is not an enemy of accessibility!(JavaScriptはアクセシビリティの敵ではない!)』でJavaScriptとアクセシビリティについてさらに詳しく書いています。彼の記事をぜひ読んでください。
前置きは十分です! 本題に入ります。
優れたフォーカス管理が不可欠
キーボードでWebサイト内を移動できるのは重要なことです。Webを利用するときにキーボードを使うユーザーはたくさんいます。ユーザーの中には体が不自由な人、目が不自由な人、両手が使えない人、そのほかの理由でマウスやトラックパッドが使えない人もいます。
キーボードでWebサイト内を移動する場合、DOM順序に従ってフォーカス可能な要素から別の要素へとジャンプします。普通はTabキー、逆方向はShift + Tabキーで実現します。リンク、ボタン、フォームもとりわけフォーカスが必要な要素です。これらはEnterキーで選択できますが、Spacebarで選択する場合もあります。さまざまな方法でフォーカス、選択できるようにすることで、要素が本来持っている優れた機能性を発揮できます。だからこそ、セマンティック要素を正しく使い、HTMLを論理的な順序で書くことは理にかなっているのです。
<p>、<h2>、<div>といった要素はデフォルトではフォーカスできません。こうしたタグを使って独自のJavaScriptコンポーネントを作ることがよくありますが、キーボードユーザーにとっては問題となる可能性があります。
フォーカスできない要素をフォーカスできるようにする
tabindex属性に整数値を追加して、フォーカスできない要素をフォーカスできるように変えられます。値を0にセットすれば要素はフォーカス可能になり、キーボード操作で要素にたどり着けるようになります。値が負の場合、JavaScriptなどによって要素はプログラム的にはフォーカスできますが、キーボード操作でのフォーカスはできません。0より大きな値も使用できますが、本来のタブオーダーが変わるアンチパターンと見なされています。
<h2 tabindex="0">A focusable heading</h2>
tabindexについて詳しく知りたい場合は、Rob Dodsonの『Controlling focus with tabindex -- A11ycasts #04(tabindexを使ったフォーカスの操作)』を参照してください。
JavaScriptで要素にフォーカスする
要素にフォーカス可能だとしても、DOM順序が正しくないことがあります。説明のために、HTML、CSS、JavaScriptを使ってシンプルなモーダルウィンドウコンポーネントを作りました(デモとコードを参照)。
Tabキーを使ってボタンにジャンプし、Enterキーを押すと、モーダルウィンドウがポップアップします。もう一度Tabキーを押すと、モーダルウィンドウの下に隠れているリンクにフォーカスがジャンプします。意図していたのは、モーダルウィンドウ内の要素にフォーカスすることでした。要素はDOM順序に従ってフォーカスされていきますが、モーダルウィンドウはドキュメントの最後にあります。これが期待通りにならなかった理由です。動作はYouTubeで確認できます。
この問題を解決するには、モーダルウィンドウをJavaScriptを使ってフォーカスします。
<div class="modal" id="modal2" tabindex="0">
...
</div>
function showModal() {
var modal = document.getElementById('modal2');
modal.focus();
...
}
更新した内容は、デモとコードを参照してください。デモで、ボタンにタブ移動してEnterキーを押し、再びタブ移動すれば上のプログラムの動作を確認できます。モーダルウィンドウにフォーカスされるはずです。
うまくいきましたが、まだ問題が2つ残っています。
Escキーを押してモーダルウィンドウを閉じるとフォーカスは消えますが、理想はモーダルウィンドウを開いたときにフォーカスしていたボタンにフォーカスが戻ることです。これを実現するためには最後にフォーカスした要素を変数に格納する必要があります。
document.activeElementを使えば現在フォーカスしている要素を取得できます。
var lastFocusedElement;
function showModal() {
lastFocusedElement = document.activeElement;
var modal = document.getElementById(modalID);
modal.focus();
...
}
ボタンを参照できるようになったので、モーダルウィンドウを閉じたときに再びそのボタンにフォーカスできます。
function removeModal() {
...
lastFocusedElement.focus();
}
別のCodePenでコードを更新しました(デモとコード参照)。アクセシビリティはかなり良くなりましたが、まだ改善の余地があります。
モーダルウィンドウを開いている間はウィンドウ内にフォーカスが維持されるのが望ましいことです。現状ではモーダルウィンドウの外へタブで移動できてしまいます。記事では詳細には扱いませんが、中途半端に終わらせないようにいわゆるキーボードトラップを使って4つ目のCodePenを作っておきました(デモとコード参照)。YouTubeで示したように、モーダルウィンドウがアクティブなかぎりウィンドウ内にフォーカスが維持されます。
最初と最後を比べると、コードがそれほど増えていないことが分かります。最後のソリューションは完璧ではありませんが、はるかに使いやすくなりました。
ほかにもアクセシビリティの高いモーダルの例や、グーグルによるtabindexの使用に関するすばらしい記事があります。キーボードテストについてさらに詳しく知りたい場合は、WebAIMのWebサイトにアクセスしてください。Webサイトには「もっとも一般的なオンラインインタラクション、インタラクションで使われる標準的なキー入力、テスト中に検討すべき事項についての補足情報」に関する一覧表があります。
フォーカス管理についてさらに多くの例を知りたい場合は、Marcy Suttonの『Focus management using CSS, HTML, and JavaScript(CSS、HTML、JavaScriptを使ったフォーカス管理』というegghead.ioの動画や、Rob Dodsonの『What is Focus? -- A11ycasts #03(フォーカスとは?)』を参考にしてください。
ボタンが必要なら<button>要素を使う
ボタンについては最初の記事ですでに書きましたが、ジェネリック要素をボタンとして使っている人が大勢いるようなので、もう少し言及するのも悪くないでしょう。
<button>や<input>の代わりに<span>や<div>をボタンとして使うことによる問題を説明するための、デモとコードを参照してください。このページでタブ移動すると、1つ目のボタンにはフォーカスできますが、2つ目のボタンにはフォーカスできません。理由は明らかで、1つ目のボタンが<button>で、2つ目のボタンが<div>だからです。<div>にtabindex="0"を追加して本来フォーカスできない要素をフォーカス可能にすることでも問題に対処できます。3つ目と4つ目のボタンが<div>であるにもかかわらずフォーカスできるのはこのためです。
<!-- Button and focusable -->
<button class="btn">I'm a button</button>
<!-- Div and not focusable -->
<div class="btn">I'm a div</div>
<!-- Still just a div, but focusable -->
<div class="btn" tabindex="0">I'm a div</div>
<!-- Button role and focusable -->
<div class="btn" tabindex="0" role="button">I'm a div</div>
divボタンは確かにフォーカス可能ですが、role属性にbuttonを追加してもまだ<div>のような反応を示します。説明のために、すべての.btn要素にシンプルなクリックイベントハンドラを追加しました(デモ参照)。各ボタンをクリックするとアラートボックスがポップアップしますが、キー(EnterまたはSpacebar)を使って同じことをするとイベントが発生するのは最初のボタンだけです。標準のボタンの反応を完全にまねるにはdivボタンにキーイベントハンドラを追加する必要がありますが、そこまでするのは不要な手間ではないでしょうか。これがボタンが必要なときに<button>要素を使うべき理由です。
さらに詳しい内容や例を知りたい場合は、Rob Dodsonの『Just use button -- A11ycasts #05(単純にボタンを使う)』や、 Adrian Roselliの『Links, Buttons, Submits, and Divs, Oh Hell』を参考にしてください。
コンテンツを動的に変えるときはスクリーンリーダーのユーザーに必ず通知
通常、スクリーンリーダーは要素にフォーカスしたときか、ユーザーがスクリーンリーダー独自のナビゲーションコマンドを使うときにしか、コンテンツを読み上げません。コンテンツを動的に読み込みDOMに挿入する場合、変更されたことが分かるのは視力のあるユーザーだけなのです。ARIAのライブリージョンは対処するさまざまな選択肢を用意しています。記事では1つの例を使って説明します。
個人データを編集し保存できるプロフィール設定ページがあるとします。保存ボタンをクリックするとページを再読み込みせずに変更を保存できます。正しく変更されたかどうかはアラートによってユーザーに通知されます。この通知はすぐに実行されることも時間がかかることもあります。実際の動作を確認するために用意した短い動画で確認してください。
変更がうまく保存されたことは見て分かりますが、聞こえません。スクリーンリーダーのユーザーは変更に気づけません。しかし、この問題にはシンプルな解決策があります。メッセージボックスのrole属性にstatusかalertを追加すると、スクリーンリーダーがその要素のコンテンツが更新されたかどうかを監視するようになります。
<div class="message" role="status">Changes saved!</div>
メッセージのテキストが変われば、新しいテキストが読み上げられます。動作は動画で確認してください。コードはCodePenにあります。
ユーザーに対して誠実に
statusとalertの違いを説明すると、alertはスクリーンリーダーがなにかを読み上げている途中でも割り込みます。逆にstatusは、スクリーンリーダーが読み上げを終わるまで待ちます。
aria-liveという別の属性があります。この属性が取る値は3つあり、off、polite、assertiveです。offはデフォルト値、aria-live="polite"はrole="status"と等しく、aria-live="assertive"はrole="alert"と等しいです。一部の良く使われる定義済みケースでは、個別に用意されたライブリージョンロールを使うほうが適切です。また、ブラウザーがroleをサポートしていない場合は、両方の属性を使っても良いでしょう。Léonie Watsonが『Screen reader support for ARIA live regions(スクリーンリーダーによるARIAライブリージョンのサポート)』でテスト結果を公開しています。
<div role="alert" aria-live="assertive"></div>
状況によって変更されたコンテンツ以外も読み上げる
デフォルトでは、同じライブリージョン内に別のコンテンツがあったとしても、スクリーンリーダーは変更されたコンテンツだけを読み上げます。しかし、ときにはテキスト全体を読み上げることが適切な場合もあります。デフォルトの動作はaria-atomic属性を使えば変更できます。この属性をtrueにセットすると支援技術が要素内のコンテンツをすべて読み上げるようになります。
Paul J. Adamは『aria-atomic & aria-relevant on aria-live regions(aria-atomicのテストケースデモ)』でさまざまなライブリージョン設定を比較しています。iOS 8.1のVoiceOverを使ったデモのテストもしており、デモの動作を確認できるように動画を録画しています。aria-atomicの使用事例をより良く理解したいなら、彼の『VoiceOver iOS 8.1 Speaking Characters Remaining aria-atomic & aria-relevant on aria-live regions(ARIAライブリージョンでaria-atomicとaria-relevantを使ってiOS 8.1のVoiceOverに残り文字数を読み上げさせる)』を参考にすることをおすすめします。
■考慮すべき点
- ライブリージョンはフォーカスの移動はせずに、テキストの読み上げのみ実施する
- alertは重大な変更だけに使うこと。ほとんどの場合statusを使うのが良い。なぜなら「politer(より誠実)」だからだ
- アラートを自動で消去するように設計しないこと。アラートがあまりに早く消えてしまう可能性があるからだ
- 個人的にテストしていたときにVoiceOverに関連する問題が見つかった。具体的には、CSSを使ったアラートの消去やアラートの動的生成がうまく動作しないことがあった。ライブリージョンをテストするときはさまざまなブラウザー、ソフトウェアを使って徹底的にテストすること
もちろん、Rob Dodsonの『Alerts! -- A11ycasts #10(アラート!)』という動画をあげていて、さらに詳しい内容や例を説明しています。Heydon PickeringのARIAの例を集めたコレクションの中にはライブリージョンの別の例があります。
ウィジェットが対応すべき使用パターンを推測する必要はない
ナビゲーションやアクセシビリティの観点でウィジェットが備えていなければならない機能すべてを考えるのは大変です。幸い、WAI-ARIA Authoring Practices 1.1というリソースの助けを借りられます。WAI-ARIA Authoring Practicesは、アクセシビリティの高いリッチインターネットアプリケーションを作るためにはどのようにWAI-ARIAを使えば良いか、という質問に答えるための指針で、推奨されるWAI-ARIAの使用パターンを解説したり、使用パターンを支える概念について紹介したりしています。
WAI-ARIA Authoring Practicesにはアコーディオン、スライダー、タブなどを作るための指針が用意されています。
アクセシビリティの高いJavaScriptコンポーネント
アクセシビリティの高いJavaScritpコンポーネントを作るためのさまざまなリソースがあります。どれもすばらしいものばかりです。
- Practical ARIA examples(ARIAの実例)
- Modaal : a WCAG 2.0 Level AA accessible modal window plugin(WCAG 2.0のレベルAAを満たす、アクセシビリティの高いモーダルウィンドウプラグイン)
- Frend: a collection of accessible, modern front-end components(アクセシビリティに優れた、先進的なフロントエンドコンポーネント群)
- The A11Y Project patterns(The A11Y Projectのパターン)
要点のまとめ
JavaScriptの長所を活用しWebサイトのアクセシビリティ向上に役立ててください。フォーカス管理に注意を払い、一般的な利用パターンを知り、DOMを操作するときはスクリーンリーダーのユーザーに配慮してください。そしてなによりも、Webサイトを誰のために作っているかを忘れず、楽しんで作ってください。
さらに詳しく学ぶ
以上です。紹介してきたコツが、アクセシビリティの高いHTMLやJavaScriptを書くのに役立つことを願っています。Heydon Pickeringに心から感謝します。彼の著書『Inclusive Front-End Design Patterns(インクルーシブなフロンエンド・デザインパターン)』がこの記事の基礎になっているからです。アクセシビリティやインクルーシブデザインについてさらに学びたい人はぜひ彼の本を読んでください。
記事の執筆に協力してくれたAdrian Roselli、文章を校正してくれたEvaに深く感謝します。
リソース
以下は本記事でリンクしているリソースの一覧です。
- A11ycasts #03:What is Focus?(フォーカスとは?)
- A11ycasts #04:Controlling focus with tabindex(tabindexを使ったフォーカスの操作)
- A11ycasts #05: Just use button(単純にボタンを使う)
- A11ycasts #10 :Alerts!(アラート!)
- 書籍: Inclusive Front-End Design Patterns
- Don’t Use Tabindex Greater than 0(tabindexで0より大きな値を使ってはいけない)
- Focus management using CSS, HTML, and JavaScript(CSS、HTML、JavaScriptを使ったフォーカス管理)
- Focusable Elements — Browser Compatibility Table(フォーカス可能な要素:ブラウザー互換性テーブル)
- Links, Buttons, Submits, and Divs, Oh Hell(リンク、ボタン、サブミット、div、Oh Hell)
- MDN:HTMLElement.focus()
- MDN:tabindex
- MDN:Keyboard-navigable JavaScript widgets(キーボードで操作できるJavaScriptウィジェット)
- The Incredible Accessible Modal Window(驚異的なアクセシビリティのモーダルウィンドウ)
- Using tabindex
- WebAIM:Keyboard Testing(キーボードテスト)
- WebAIM:Keyboard Accessibility(キーボードのアクセシビリティ)
- WAI-ARIA:aria-atomic
※本記事は、当初Mediumで公開されたものです。
(原文:Writing JavaScript with Accessibility in Mind)
[翻訳:薮田佳佑/編集:Livit]