アクセシビリティー:キーボードは貴方の友達です
こんにちは、ゼノフィnakamuraです。
はじめに
「アクセシビリティー」という言葉は非常に幅広い意味を持ち、初めてでくわした時には威圧感を感じるかもしれません。そういった初めての遭遇の多くは、新しい業務要件からくることが多かったりするのではないでしょうか。それは、開発者が既存のアプリケーションの操作性を向上するということです。アクセシビリティーを考える上で、頭に入れておかなければいけない側面がいくつもあります。例えばアシスティブテクノロジー (支援技術 )やウェブ標準などです。無料のアシスティブテクノロジー製品は数多く存在しますが、それらの使用方法を理解するには時間がかかります。
こういった問題に慣れる (そしてその通りにデザインしようとする) ことは良いことではありますが、そういったバリアも存在しない重要なアクセシビリティーがひとつあります。キーボードインタラクションです。キーボードマネージメント自体がアプリケーションにとってはある一定のレベルのアクセシビリティーではありますが、常に最低基準でもあります。これは ARIA Practices の草案にもそう記されています:
「あなたのウィジェットがキーボードでもアクセスできることは非常に大事です。むしろアプリケーション内にはマウスの動作一つに対して同じものがキーボードにも用意されていなければなりません。」
正しく行われれば、キーボードマネージメントは障害を持っていない人にとっても価値があるものになります。パワーユーザーはキーボード、マウス、タッチスクリーンなどをいったりきたりすることなく、いくつかのキーストロークでアプリケーション内を動きまわることができるでしょう。
フォーカス
フォーカスのコンセプトは特別新しいものではありませんが、キーボードマネージメントの基礎となるものです。新しい、あるいは自明でなかった情報は、いかにフォーカスがユーザーにとって大事であるかということです。
「キーボードアクセシビリティーにとって一番大事なことは信頼でき、正確なフォーカスを表示できるかということです。作者はスクリプト内で、ビジュアルとプログラムによるフォーカスを管理し、アクセス行動を観察する責任があります。スクリーンを読む人や、キーボードオンリーのユーザーはフォーカスを頼りに、キーボードを使ってインターネットアプリケーションを実行します。」[ W3C ARIA Practices の草案]
Ext JS 5.0.1 で、この要件に対応するコアコンポーネントに対して、必要なスタイルを追加しました。また、テーマでフォーカスの表示を簡単に調整できるように、Sass 変数を追加しました。

キーボード入力
フォーカスのコンセプトは全てキーボード入力に関することです。ユーザーがキーボードで入力すると、押されたキーは現在フォーカスされているエレメントに向けられます。これは input エレメントでは当たり前のように感じるかもしれませんが、そういったエレメントに限定されたものではありません。例えば、div エレメントが tabIndex をアサインされた後にフォーカスを受けると、キーボードイベントはそのエレメントに届けられます。インプットエレメントとは違い、これらのイベントにデフォルトアクションは関連づけられていません。
フォーカスを受け取る
). フォーカスを受けるのは多くのコンポーネントにとって理に適っています。 Ext.Component の focusable プロパティがコンポーネントがフォーカスを受けることができるかを示しています。これはコンポーネントと関連づいている DOM (例えば TextField の input エレメントなど ) によるものです。
コンポーネントがフォーカスを受けることができるかは、レンダー時に作られる DOM によって判断されます。多くのエレメントは最初からフォーカスを受けることができます ( anchor や input エレメントなど )。デフォルトでフォーカスを受けることができないエレメントは tabIndex 属性を使うことで可能になります。
コンポーネントがそれに適した DOM 構造を持っていれば、 focusable クラスプロパティを true にセットします。こうすると、コンポーネントの focus や blur イベントをリッスンするように指示します。コンポーネントはたくさんのエレメントを含むことができるため、そういったコンポーネントは getFocusEl メソッドを実装しなければ、レファレンスをコンポーネントのフォーカスエレメントに返すことができないかもしれません。これはデフォルトでプライマリーエレメントです。
フォーカスの変更
フォーカスはエレメント間でマウスクリックやキープレスによって動くことがあります。mousedown イベントがフォーカス可能なエレメントで発生すると、ブラウザのデフォルトアクションはフォーカスを与えることです。しかし、キーボードの場合、複数の方法でフォーカスの変更をすることができます。
キーボードを通してフォーカスを変える最初の方法は TAB キー (SHIFT で変更することもあります )。TAB キーはフォーカスを「次」のタブできるエレメントに動かします。もし SHIFT も押されていれば、一つ「前」のタブできるエレメントに動きます。ここで大事なのはフォーカスできる (focusable) ではなくタブできる (tabable) という言葉を使っていることです。いくつかのエレメントはプログラム的にとか、mousedown イベントでフォーカスはできても、TAB キーでは移動できない場合があります。
また、矢印キー (左、右、上、下 )を使って子アイテム間を移動するといったような要件がある場合もあります。これはツールバーやメニューなどの特別なコンテナで起きることですが、これについては後ほどふれたいと思います。
フォーカスを変更した場合の副作用
フォーカスが変更されると、大きな副作用も現れてしまいます。一番わかりやすいのは美的感覚ですが他にもあります。
- バインド – 双方向バインドを持った有効な入力値は書き戻されます。
- 折りたたみ – ComboBox などのピッカーフィールドでは非表示にすべきピッカーが開いたままになるかもしれません。
- 委任 – いくつかのコンポーネントはフォーカスを受けると、瞬時に他にフォーカスを移してしまうかもしれません。例は、 defaultFocus コンフィグを参照ください。
- エディット – エディタ (グリッドセルエディタなど )内で使用されば場合、フォーカスを失う時にフィールドが値を書き戻す場合があります。
- バリデーション – フィールドから離れる時に入力のバリデーションが行われます。
これらの行動は主にフォーカスを失うコンポーネントに向けられます。DOM の言葉では、blur を受けているコンポーネントと言う風に言います。
blur の進化
過去のバージョンの Ext JS では、blur イベントこそが前述の副作用の引き金となったイベントです。しかし多くの場合、blur イベントはタスクにあっていなく、フレームワークが補正を行わなければなりませんでした。これは、focus と blur イベントはバブルしないからというのも理由の1つでした。これらの複数エレメントのイベントを処理するには、各エレメントに両方のイベントのためにリスナーを加えるしかありません。バブリングよりも問題が多いのは、Firefox がこれらのイベントに relatedTarget を正しく伝えないことです。
DOM 標準ではこれを修正しようと、新しく focusin と focusout イベントを使って進化を遂げようとしています。これらのイベントはまだ全てに対応しているわけでなく (仮にもしされていても )、それらはフロート状態のコンポーネント (ボタンの所有しているメニューなど )には有効ではありません。必要なものは focusin と focusout のようにバブルするイベントでありながら、Ext JS コンポーネント階層を理解しているものです。
focusenter と focusleave
focusenter と focusleave という、これらの新しいコンポーネントイベントはそういった問題を解決するためにデザインされました。実際にデモを見てみましょう。

以下のようなコンポーネント階層があるとしましょう。
- window
- tbar
- A (button)
- B (button)
- C (button)
- tbar
-
- bbar
- X (button)
- Y (button)
- Z (button)
- bbar
ボタンを操作すると、コンソール内でイベントを見ることができます。
1. Click button A --> enter A from ext-element-1 --> enter tbar from ext-element-1 --> enter window from ext-element-1 2. Press RIGHT arrow (focus moves to B) <-- leave A to B --> enter B from A 3. Press TAB (focus moves to X) <-- leave B to X <-- leave tbar to X --> enter X from B --> enter bbar from B |
ステップ1 では、focusenter イベントが ボタン A に到着し、tbar にバブルアップし、最後にウィンドウに到着したことがわかります。ボタンB へのナビゲーションは tbar 内にあるので、これらのイベントは tbar もしくは ウィンドウには届きません (フォーカスはそれらに出入りしていないため )。最後に、ボタンX まで TAB で行くと、フォーカスが tbar を離れ、bbarに入りながらもウィンドウコンポーネントの中に残ります。
また、デモではボタンにメニューを割り当てています。これらのメニューを開き、メニューアイテムにフォーカスした場合、それらを所有しているボタンの子供として扱われることがわかります。これらはメニューエレメントがドキュメントボディにレンダリングされ、ボタンの DOM 階層の外になっていても有効です。
最初のパスと繰り返されたステップでは、表示されるメッセージには多少の違いがあるかもしれません。しかし、肝心なのはこれらのイベントが常に「関係する」コンポーネントを用意してくれるということです。
onFocusEnter と onFocusLeave
これらの新しいコンポーネントメソッドはフォーカスがコンポーネントに入るか離れる時にコールされます。もし focus や blur に反応するロジックを含んだカスタムコンポーネントやビューがあるのならば、それらのロジックをこれらのメソッドに移すことを検討したほうが良いかもしれません。focus や blur のプロセスはもちろんまだサポートしていますし、そちらの方が使いやすい場合もあります。しかし、子コンポーネントを作らなければいけなく、それらがフロートコンポーネントであった場合、これらの新しいメソッドのほうが適切なタイミングを提供してくれます。
Focus and Containers フォーカスとコンテナ
多くの場合、ユーザーは TAB と SHIFT+TAB を押すことでコンポーネント間を移動します。しかし、ある場面では矢印キーを利用しなければいけない要件もあります。例えば、メニュー、ツールバー、RadioGroup などのコンテナでは一回だけの TAB キープレスで入退場を期待されています。これはユーザーがより早くページ内をナビゲートするためにあり、いちいちツールバーボタンの中を1つ1つ通らなくていいようにするためにあります。
Ext JS はこれを FocusableContainer mixin でサポートしています。この mixin は直前にフォーカスがあったアイテムをトラックし、フォーカスが戻った時に復元します。TAB が正しくコンテナから離れ、フォーカスが復元されることを保証するため、この mixin は、子アイテムの tabIndex を管理します。
フォーカスをセットする
フォーカスを常に維持することと、生産的なワークフローを保証するためにロジカルなフォーカス移行を提供するのは非常に重要なことです。例えば、新しいフォームを開く場合フォーカスを最初にインプットフィールドに置くことが推奨されます。これはユーザーがすぐにそのフィールドに移動することなく入力を始めることができるようにするためです。
他の場合にも、プログラムでフォーカスをセットする場面があります。 例えば、 Ext.Msg を使って Yes/No プロンプトを表示するときです。 この種の介入が必要になる場合は、できるかぎり前にフォーカスのあった場所に戻すことが重要です。 Window クラスには、フォーカスを戻すのをアシストするロジックがあります。 フォーカスを操作する必要がある場合は、やりなおす処理が必要なことを忘れないでください。
マスキング
マスキングは、フォーカスを扱う場合に気をつけなければいけない状況のひとつです。例えば、パネルにサーバーに AJAX リクエストを起こすボタンを持っていたとします。そういった時は、パネルをマスクすることでリクエストが終わるまでボタンが押されないようにすることが適切な場合が多いです。マスキングエレメントは単体でもマウスクリックを防ぐのに有効ですが、パネルのコンテンツへのキーボードアクセスを防ぐにはさらに必要なことがあります。
マスクが表示された場合、全ての子アイテムは一時的にタブすることが不可になっています (tabIndex を -1 にセットする )。それに加え、フォーカスがマスクされたコンポーネントにある場合、フォーカスはマスクに動きます。1つ前にフォーカスされたアイテムがマスクが外された時にもすぐ近くにあると想定して、フォーカスはこのアイテムに戻ります。子アイテムのタブ可属性も復元されます。
結論
タッチインターフェースにたくさんの注目が集める今日、タッチが全ての過去のインターフェースと入れ替わるという印象を簡単にもってしまうかもしれません。しかし、開発者だけがキーボードユーザーとうわけではありません。ユーザーも我々と同じようにキーボードを必要としています。我々のアプリがキーボードから動かすことができれば、ユーザーに生産性や効率性を提供することができます。また、障害を持った人々へも門戸を開く事ができます。
どれくらい物事がうまく行ってるかを簡単に測る方法があります:1つのタスクをこなすのに何回マウスを触るか、スクリーンを触るかを数えればいいだけのことです。