この仕様はかなり長いです。 特定の領域を読みやすく、集中しやすくするために、 いくつかのチェックボックスを下に用意しています。 チェックを入れると仕様の一部が非表示になります。 これはあくまで読みやすさのための補助機能であり、 仕様そのものは完全な文書のままです。
1. はじめに
このセクションは規範的ではありません。
従来、多くのブラウザにはフォーカスを方向的に移動する機能がありませんでした。 一部のTVブラウザなどでは、他の入力手段がないため、矢印キーでフォーカスを移動できるようになっています。
他にも、Shift
キーと矢印キーの組み合わせなど、異なるキー操作で空間ナビゲーションを制御できるものもあります。
このページ上で方向的に移動できる能力は、空間ナビゲーションと呼ばれます。
空間ナビゲーションは、グリッド状レイアウトや、
他の主に非線形レイアウトを使って構築されたウェブページで役立ちます。
下図はグリッドレイアウトで写真ギャラリーを表しています。
Tab
キーで画像間のフォーカスを移動しようとすると、
目的の画像要素に到達するまで何度もキーを押す必要があります。

また、空間ナビゲーションは
フォーカス可能な要素の位置に応じてフォーカスを移動するため、ユーザーにとって予測可能な要素へフォーカスを移動します。
ページ上の要素がソース順とは独立して配置されている場合もあります。
そのため、 とは異なり、Tab
キーによる逐次ナビゲーションは
フォーカス移動が予測しづらくなります。
矢印キーは空間ナビゲーションの制御に自然に適していますが、 これまでどの仕様もその動作や制御方法を記述していませんでした。 この仕様は空間ナビゲーションの処理モデルを導入し、 著者が空間ナビゲーションの動作を制御・上書きできるAPIも提供します。
注: この仕様のJavaScriptイベントやAPIなどは、 逐次ナビゲーションにも拡張できるため、 キーボードナビゲーション全体で一貫性のある明確なモデルを持つことができます。
注: 一般原則として、 キーボードナビゲーション、とくに空間ナビゲーションはJavaScriptなしでも 利用・制御できるべきです。 そのため宣言的な方法が推奨されます。 空間ナビゲーションはレイアウトに依存するため、 CSSが空間ナビゲーション関連の制御を定義する適切な仕組みとなります。 ただし、Extensible Web Manifesto[EXTENSIBLE]の精神に則り、 著者が問題領域の実験や探求を行えるよう適切なJavaScriptプリミティブを提供することも重要だと考えます。 こうしたJavaScript利用を通じて得られるフィードバックや経験を元に、 より宣言的な機能を今後追加する可能性もあります。
注: 一部の機能はリスクありとマークされています。 本仕様の編集者は、これらの機能がユーザーや著者体験の重要な部分を構成すると考えていますが、 これらを実装せずとも仕様のコア機能は実装可能なため、 実装者が初期実装の範囲を縮小するため優先度を下げる可能性があります。 これらの機能も実装されることが望ましいですが、 最初は実装されない可能性があることを考慮し、リスクありとしています。
2. モジュールの相互作用
この文書はInfra Standard[infra]に依存します。
キーワード "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", "OPTIONAL" は RFC 2119 [RFC2119]の定義通りに解釈されます。
3. 概要
このセクションは規範的ではありません。
UAで定義された仕組み
(通常は矢印キー、場合によってはShift
やControl
などの修飾キーとの組み合わせ)
を使い、ユーザーはUser Agentに特定の方向へのナビゲーションを要求できます。
これにより、
現在の場所から要求された方向の新しいフォーカス可能な項目へフォーカスが移動するか、
適切な項目がなければスクロールされます。
より具体的には、 User Agentはまず、指定された方向の 現在の空間ナビゲーションコンテナ内(初期状態ではルート要素、スクロール可能要素、iframeですが、空間ナビゲーションコンテナはspatial-navigation-contain プロパティを使うことで他の要素にも指定可能です)で、表示されていてフォーカス可能な項目を探します。
もし見つかった場合は、その方向に最適な項目を選び、フォーカスを移動します。
見つからなかった場合は、要求された方向に空間ナビゲーションコンテナをスクロールします。 これによってフォーカス可能な要素が表示され、 次回同じ方向への空間ナビゲーション操作時にフォーカスの対象となります。
もし空間ナビゲーションコンテナがスクロールできない場合、 たとえばスクロール可能な要素でない場合や既にその方向の最大までスクロールされている場合は、 User Agentは次の (親要素)を選び、 上記の処理を再帰的に繰り返して、 フォーカスまたはスクロールできる要素が見つかるか、ルート要素に到達するまで続けます。
注: この処理モデルの結果として、 逐次ナビゲーションと空間ナビゲーションで到達可能な要素はほぼ同じになります。 スクロール可能要素のビューポート外にある要素は、 空間ナビゲーションでスクロールされて表示された場合のみ到達可能となります。 したがって、デフォルトでスクロールして表示できない要素には到達できません。
preventDefault()
を呼ぶことで)
直前のアクションを防止したり、
代替アクション(focus()
メソッドで任意の要素にフォーカスするなど)
を提供できます。
こうした代替アクション記述を支援し、Extensible Web原則に沿ってプラットフォームプリミティブを公開する一環として、 この仕様では基盤モデルの主要な構成要素にアクセスできるJavaScript APIも定義します。
JavaScript APIの詳細は§ 5 JavaScript API、 各種イベントの詳細は§ 6.2 ナビゲーションイベントタイプ、 CSSプロパティの詳細は§ 9 宣言的手法による空間ナビゲーションの制御を参照してください。


図2の左側では「Box 2」がフォーカスされています。
ArrowDown
キーを押すと、
「Box 3」がscrollport
内で表示されているため、スクロールせずに空間ナビゲーションコンテナ内で「Box 3」にフォーカスが移動します。




図3の1枚目では「Box 3」の下にscrollport内で表示されている要素はありません。
そのため、ArrowDown
を押すと2枚目のように下方向にスクロールします。
さらにArrowDown
キーを押すことで「Box 4」がscrollport内に現れ、
追加でもう一度ArrowDown
を押すと4枚目のようにフォーカスが移動します。
この例のマークアップは次の通りです:
#scroller { width : 700 px ; height : 700 px ; overflow-x : hidden; overflow-y : auto; } .box { width : 150 px ; height : 110 px ; background-color : blue; } .box:focus { background-color : red; }
< div id = "scroller" > < div class = "box" tabindex = "0" > Box 1</ div > < div class = "box" tabindex = "0" > Box 2</ div > < div class = "box" tabindex = "0" > Box 3</ div > < div class = "box" tabindex = "0" > Box 4</ div > </ div >
4. 空間ナビゲーションのトリガー
ユーザーが指定した方向に空間ナビゲーションをトリガーした場合、 ユーザーエージェントはその方向で空間ナビゲーション手順を実行しなければなりません。
この仕様では、ユーザーエージェントが空間ナビゲーションをトリガーするために ユーザーにどのようなUIメカニズムを提供するべきかは定義しません。 これは意図的にユーザーエージェント側の判断に委ねられています。
ユーザーエージェントは仕様で定義された処理モデルやAPIを実装できますが、 この仕様では ユーザーエージェントが、APIを使わず直接ユーザーが空間ナビゲーションをトリガーできる手段を 提供することを推奨しています。
注: 逆に、著者は空間ナビゲーションが APIを呼び出していなくても ユーザーの操作に応じてユーザーエージェントによってトリガーされる可能性があることを 想定しておくべきです。
空間ナビゲーションをトリガーするための実際のメカニズムが何であれ、 以下の要件が適用されます:
-
ユーザーが空間ナビゲーションをトリガーするために使うメカニズムが 通常
UIEvent
を発火する場合は、 空間ナビゲーション手順を実行する前に イベントを発火し、そのイベントのcanceled flag が設定された場合は手順を実行してはなりません。ゲーム機ではDパッドを押すことで空間ナビゲーションがトリガーされる場合があります。 このときArrowDown
、ArrowLeft
、ArrowRight
、ArrowUp
のいずれかにキーが設定されたkeydownイベントが発火し、 キャンセルされなければ空間ナビゲーション手順(関連するNavigationEvent
の発火も含む)が実行されます。デスクトップPCのユーザーエージェントがキーボードの矢印キーで空間ナビゲーションをトリガーする場合も、同じシーケンスになります。
-
ユーザーが空間ナビゲーションをトリガーするために使うメカニズムが 文脈によって他の動作も実行する場合、 その文脈ではユーザーエージェントは他の動作を優先し、 空間ナビゲーションの代わりにそれを実行すべきです。 両方を同時にトリガーしてはなりません。
ユーザーエージェントが修飾キーなしの矢印キーで空間ナビゲーションをトリガーし、 編集可能な要素がフォーカスされている場合は同じ矢印キーでテキストカーソルの移動を行う場合、 デフォルトでは矢印キーによるカーソル移動を優先すべきです。 フォーカスされた要素が編集不可か、編集可能でもカーソルが指定方向にこれ以上移動できない場合のみ 空間ナビゲーションがトリガーされます。スクロールについては例外です: 空間ナビゲーション自体がスクロール(およびフォーカス移動)を扱うため、 ユーザーエージェントは空間ナビゲーションとスクロール動作を 別々のメカニズムで同時にトリガーしてはなりません。 ただし、異なるモード間の切り替えや異なるUIメカニズムで両方を提供することは可能です。
5. JavaScript API
5.1. プログラムによるナビゲーションのトリガー
navigate()
メソッドは、著者がプログラムから空間ナビゲーションをトリガーできるようにします。
これは、ユーザーが手動で(例えばブラウザで矢印キーを押すなど)空間ナビゲーションをトリガーしたのと同じように動作します。
注: このメソッドは手動ナビゲーションと同じ処理モデルをトリガーするため、 同じ結果が得られるはずです。 同じイベントチェーンが発火し、同じ要素がスクロールまたはフォーカスされます。
注: 著者はこれを使って、 ユーザーエージェントで割り当てられたUIメカニズム以外(異なるキーへの割り当てや 画面上のクリック可能な方向パッド、UIイベント以外のイベントなど)で空間ナビゲーションを トリガーすることもできます。 また、無限スクロールのように非同期操作のため途中でナビゲーションを中断し、 その後再開したい場合にも利用できます。
注: このAPIはテスト用途にも便利です。 ベンダー固有のUI慣習に依存しない空間ナビゲーションをトリガーするのは困難なためです。
enum {
SpatialNavigationDirection ,
"up" ,
"down" ,
"left" , };
"right" partial interface Window {void navigate (SpatialNavigationDirection ); };
dir
navigate(dir)
メソッドが呼ばれたとき、
ユーザーエージェントは次の手順を実行しなければなりません:
-
方向dirが
"up"
、"down"
、"left"
、"right"
のいずれかであれば、 dirで空間ナビゲーション手順を実行する。
このAPIの名称は議論中です <https://github.com/w3c/csswg-drafts/issues/3387>
5.2. 低レベルAPI
これらのAPIは、処理モデルに密接に従った低レベルの構造体として設計されています。 そのため、空間ナビゲーションの動作を拡張・上書きしたい著者にとって使いやすくなっています。
enum {
FocusableAreaSearchMode ,
"visible" };
"all" dictionary {
FocusableAreasOption FocusableAreaSearchMode ; };
mode dictionary {
SpatialNavigationSearchOptions sequence <Node >?;
candidates Node ?; };
container partial interface Element {Node getSpatialNavigationContainer ();sequence <Node >focusableAreas (optional FocusableAreasOption );
option Node ?(
spatialNavigationSearch SpatialNavigationDirection ,
dir optional SpatialNavigationSearchOptions ); };
options
注: 方向の表現方法は、今後必要に応じて4方向以上への拡張が可能です。 より多くの方向キーワードや数値による角度指定も追加できます。
注: focusableAreas()
およびgetSpatialNavigationContainer()
メソッドはリスクありです。
これらのメソッドが呼び出されたとき、 ユーザーエージェントは以下に示す手順を実行しなければなりません:
getSpatialNavigationContainer()
-
-
要素の最も近い祖先である空間ナビゲーションコンテナを返す。 ただし、最も近い がビューポートである場合はdocumentを返す。
注: 要素自体が空間ナビゲーションコンテナの場合も、
getSpatialNavigationContainer()
は最も近い (要素自身ではない)を返します。 -
focusableAreas(option)
-
-
optionが存在し、その値が
all
と等しい場合は visibleOnlyをfalse
に、そうでなければtrue
にする。 -
要素内でvisibleOnlyを引数としてフォーカス可能エリアの検索を行い、その結果をareasとする。
-
areasを返す。
-
focusableAreas()
で取得する方法を示します。
メソッドが空間ナビゲーションコンテナを見つけた場合、その内部のフォーカス可能エリアも再帰的に検索します。
このメソッドのmode
属性値がvisible
なので、
scrollportの外にある要素は結果から除外されます。
< body >
< button ></ button >
< div style = "width:300px; height:200px; overflow-x: scroll;" >
< button style = "left:25px;" ></ button >
< button style = "left:150px;" ></ button >
< button style = "left:350px;" ></ button >
</ div >
</ body >
const focusableAreas = document. body. focusableAreas({ mode: 'visible' });
focusableAreas && focusableAreas. forEach( focusable => {
focusable. style. outline = '5px solid red' ;
});
下図はこのコードの実行結果です。

spatialNavigationSearch(dir, options)
-
-
directionをdirの値とする。
-
containerを以下のように決定する:
-
optionsの
container
属性値がnullでない場合、-
自身が空間ナビゲーションコンテナであれば自身。
-
そうでなければ最も近い空間ナビゲーションコンテナ祖先。
-
-
そうでない場合は、要素の最も近い空間ナビゲーションコンテナ祖先。
-
-
areasを以下のように決定する:
-
optionsの
candidates
属性値がnull
でなければその値、 -
そうでなければcontainer内でフォーカス可能エリアの検索の結果。
-
-
要素からdirection方向でcontainer内のareasの中から最適な候補の選択の結果を返す。
-
注: containerも候補リストも指定されていない場合は、
最も近い空間ナビゲーションコンテナ祖先内の
表示フォーカス可能エリアのみを検索します。もし存在しない場合は祖先チェーンをさらに遡らず、結果はnull
になります。
6. ナビゲーションイベント
6.1. インターフェース NavigationEvent
NavigationEvent
インターフェースは、空間ナビゲーションに関連した特定のコンテキスト情報を提供します。
NavigationEvent
インターフェースのインスタンスを作成するには、NavigationEvent
コンストラクターを使用し、
オプションの NavigationEventInit
辞書を渡します。
[Exposed =Window ]interface :
NavigationEvent UIEvent {(
constructor DOMString ,
type optional NavigationEventInit );
eventInitDict readonly attribute SpatialNavigationDirection ;
dir readonly attribute EventTarget ?; };
relatedTarget dictionary :
NavigationEventInit UIEventInit {SpatialNavigationDirection ;
dir EventTarget ?=
relatedTarget null ; };
6.2. ナビゲーションイベントタイプ
このセクションおよびそのサブセクションは規範的ではありません。
ナビゲーションイベントタイプの概要は以下の通りです。 詳細な規範的記述は § 8 処理モデル を参照してください。
6.2.1. navbeforefocus
このイベントは、最適な候補の選択結果が有効な場合に発生します。
タイプ | navbeforefocus
|
---|---|
インターフェース | NavigationEvent
|
バブル | はい |
キャンセル可能 | はい |
イベントの属性 |
|
ユーザーエージェントは、空間ナビゲーションがフォーカスを移動する前に navbeforefocus イベントを発火します。
イベントターゲットは現在フォーカスされている要素であり、
relatedTarget
はこれからフォーカスされる要素です。
navigation-override が eventTarget の ノードドキュメントで無効な場合、 アクティブドキュメントの トップレベル閲覧コンテキストのオリジンに対して、このイベントは発火されません。
ArrowRight
キーを押した場合の挙動を示します。
説明を簡単にするため、この例では空間ナビゲーションが矢印キーでトリガーされるユーザーエージェントを想定しています。
イベントタイプ | KeyboardEvent .key
| 備考 | |
---|---|---|---|
1 | keydown | ArrowRight
| 空間ナビゲーションを有効化できるキー(矢印キーなど)でなければならない。そうでなければ空間ナビゲーションは発動しない。 |
2 | navbeforefocus | 空間ナビゲーションの候補が null でなければ送信され、そうでなければ生成されない。
| |
3 | focusin | ターゲット要素がフォーカスを受ける直前に送信される。 | |
4 | focus | ターゲット要素がフォーカスを受けた後に送信される。 |
document. addEventListener( 'navbeforefocus' , e => {
e. preventDefault();
let nextTarget = e. relatedTarget;
if ( isSpatialNavigationContainer( nextTarget)) {
const areas = nextTarget. focusableAreas();
if ( areas. length > 0 ) {
nextTarget = nextTarget. spatialNavigationSearch( e. dir, { candidates: areas });
}
}
nextTarget. focus();
});
function isSpatialNavigationContainer( element) {
return ( ! element. parentElement) ||
( element. nodeName === 'IFRAME' ) ||
( isScrollContainer( element)) ||
( isCSSSpatNavContain( element));
}
6.2.2. navnotarget
このイベントは、空間ナビゲーションが現在の空間ナビゲーションコンテナで候補を検索するために ツリーを上がる直前、または がスクロール可能で それ以上スクロールできない場合に発生します。
内で候補を見つけられなかった場合に 最も近い祖先タイプ | navnotarget
|
---|---|
インターフェース | NavigationEvent
|
バブル | はい |
キャンセル可能 | はい |
イベントの属性 |
|
ユーザーエージェントは、イベントターゲットを現在フォーカスされている要素、
relatedTarget
をイベントターゲットの空間ナビゲーションコンテナとして初期化し navnotargetイベントを発火します。
navigation-override が eventTarget の ノードドキュメントで無効な場合、 アクティブドキュメントの トップレベル閲覧コンテキストのオリジンに対して、このイベントは発火されません。
ArrowDown
キーを押した際の UI
Events §event-order の挙動を示します。
説明を簡単にするため、この例では空間ナビゲーションが矢印キーでトリガーされるユーザーエージェントを想定しています。

イベントタイプ | イベントターゲット | relatedTarget
| 備考 | |
---|---|---|---|---|
1 | keydown | #box2
| N/A | 空間ナビゲーションを有効化できるキー(矢印キーなど)でなければならない。そうでなければ空間ナビゲーションは発動しない。 |
2 | navnotarget | #box2
| #scrollContainer
| #scrollContainer に候補が存在せず、スクロールもできない場合に送信される。そうでなければ生成されない。
|
3 | navbeforefocus | #box2
| #box3
| #container 内の候補が null でなければ送信され、そうでなければ発火しない。
|
4 | focusin | #box3
| #box2
| ターゲット要素がフォーカスを受ける直前に送信される。 |
5 | focus | #box3
| #box2
| ターゲット要素がフォーカスを受けた後に送信される。 |
この例の結果は次の図の通りです:

この例のマークアップ:
#container { width : 900 px ; height : 1400 px ; } #scrollContainer { width : 700 px ; height : 700 px ; overflow-x : hidden; overflow-y : auto; } .item { width : 150 px ; height : 110 px ; background-color : blue; } .item:focus { background-color : red; }
< div id = "container" > < div id = "scrollContainer" > < div id = "box1" class = "item" tabindex = "0" > Box 1</ div > < div id = "box2" class = "item" tabindex = "0" > Box 2</ div > </ div > < div id = "box3" class = "item" tabindex = "0" > Box 3</ div > </ div >
ただし、逐次ナビゲーションやマウス操作、
focus()
のプログラム呼び出しでは外にフォーカス移動できます。
scrollContainer. addEventListener( 'navnotarget' , e => {
let nextTarget = null ;
const verticalDir = [ 'up' , 'down' ];
const candidates = e. relatedTarget. focusableAreas({ 'mode' : 'all' });
// ナビゲーション方向がY軸の場合のみデフォルト動作を防止する
if ( verticalDir. includes( e. dir) && ( candidates. length > 0 )) {
e. preventDefault();
if ( e. dir === 'down' ) {
nextTarget = candidates[ 0 ];
} else if ( e. dir === 'up' ) {
nextTarget = candidates[ candidates. length- 1 ];
}
nextTarget. focus();
}
});
7. navigation-override ポリシー制御機能
navigation-override ポリシー制御機能は、 ページ著者が空間ナビゲーションの挙動を制御したり、完全にキャンセルできる仕組みの利用可否を制御します。
-
この機能の名前は "
navigation-override
" です。 -
デフォルト許可リストは "
self
" です。
詳細は § 8.3 ナビゲーション で定義されていますが、 ドキュメントでnavigation-overrideが無効化されている場合、 ナビゲーションイベント(§ 6 ナビゲーションイベント参照)は発火しません。
注: これは悪意のあるiframeがフォーカスを乗っ取るためにこれらのイベントを利用するのを防ぐためです。 空間ナビゲーション以前の時代から、著者がユーザーのフォーカス制御を妨害する他の手段が存在することも認識しています。 それでも攻撃面を広げないようにする試みは価値があると考えますが、 実際には既にこうした攻撃が容易な可能性もあるため、完全な防止は難しいかもしれません。 実装や攻撃対策の経験に基づくフィードバックも歓迎します。
8. 処理モデル
このセクションでは対応する規範的な挙動を定義し、 挙動の完全な定義に必要なだけ詳細に踏み込みます。
8.1. 用語集
空間ナビゲーションの処理モデルを説明するために、以下の用語定義が記載されています。 定義内のリンク先も参照してください。
boundary box(境界ボックス)は次のように定義されます:
-
対象が点の場合、その点自身がboundary boxです。
-
対象がボックスまたはボックスフラグメントの場合、 そのボックスやフラグメントのborder boxです。
-
対象がfocusable area(要素でない)なら、そのfocusable areaの軸揃えbounding boxです。
inside area(内側領域)は次のように定義されます:
-
対象がボックスまたはボックスフラグメントなら、boundary boxです。
注: オブジェクトが画面外の場合、inside areaは最も近い表示中の祖先コンテナとすべきです。
CSSは「border-radiusなど角形状プロパティを考慮したborder box」に対応する用語を持つべきです。<https://github.com/w3c/csswg-drafts/issues/2324>
search originは次のターゲット検索の起点です。
spatial navigation starting pointは、ユーザーエージェントが設定する次のターゲット検索の起点で、初期状態では未設定です。値は要素または点です。
注: 例えば、ユーザーがドキュメント内容をクリックした場合にその位置をstarting pointに設定し、空間ナビゲーションや他の手段でフォーカスが移動した際に解除することができます。
ユーザーエージェントがspatial navigation starting pointと 逐次フォーカスナビゲーション起点の両方を設定する場合、これらは別々に設定してはなりません。
8.2. 要素のグループ化
空間ナビゲーションの処理モデルはドキュメントのレイアウトとフォーカス可能要素の相対位置に基づいて動作しますが、 ユーザーエージェントはローカルな論理グループ内で要素を優先的に探し、適切なものが見つからない場合のみグループ外も探索する必要があります(詳細は§ 8.3 ナビゲーション参照)。
このようなグループ化を 空間ナビゲーションコンテナと呼びます。
初期状態では空間ナビゲーションコンテナは次によって成立します:
-
閲覧コンテキストのビューポート(トップレベル閲覧コンテキストに限定されません)
8.3. ナビゲーション
空間ナビゲーションの処理モデルは、空間ナビゲーションの一般的な動作を説明します。
この図は規範的なものではありません。 このセクションでさらに定義される処理モデルの概要を示しています。 spatial-navigation-actionプロパティが 初期値autoである場合を前提としています。
-
searchOriginを検索起点の設定の結果とする。
-
eventTargetがDocumentまたはdocument elementの場合、 eventTargetをbody要素(nullでなければ)または そうでなければdocument elementに設定する。
-
- eventTargetがスクロールコンテナであり、 そのspatial-navigation-actionプロパティの算出値が scrollかつ eventTargetが手動スクロール可能である場合、 その要素を方向スクロールし、終了。
-
それ以外の場合、eventTargetがスクロールコンテナまたはdocumentの場合
-
candidatesをフォーカス可能エリアの検索を eventTarget内で実行し、visibleOnly引数は spatial-navigation-actionプロパティの算出値が focusなら
false
、 それ以外ならtrue
とする。 -
- spatial-navigation-actionプロパティの算出値が focusでなく、 eventTargetが手動スクロール可能なら、 方向スクロールし、終了。
-
それ以外、candidatesが1つ以上ある場合:
-
bestCandidateを最適な候補の選択を candidates内でdirectionとsearchOriginで実行した結果とする。
-
navbeforefocusイベント発火を eventTarget、direction、bestCandidateに対して行う。
-
フォーカス手順をbestCandidateに対して実行し、終了。
-
- それ以外は次のステップへ。
-
- それ以外は次のステップへ。
-
containerをeventTargetの最も近い祖先で空間ナビゲーションコンテナであるものとする。
-
ループ: candidatesをフォーカス可能エリアの検索を container内で実行し、visibleOnly引数は spatial-navigation-actionプロパティの算出値が focusなら
false
、それ以外ならtrue
で、eventTargetを除外 とする。 -
candidatesが空の場合:
- spatial-navigation-actionプロパティの算出値が focusでなく、 containerがスクロールコンテナで 手動スクロール可能なら、 方向スクロールし、終了。
- それ以外の場合、
-
navnotargetイベント発火を eventTarget、direction、containerに対して行う。
-
-
containerがdocument elementかつ トップレベル閲覧コンテキストの場合は終了。 ユーザーエージェントはdirectionに従い自身のコントロール(あれば)にフォーカスを移してもよい。
-
それ以外でcontainerがdocument elementかつ 入れ子閲覧コンテキストの場合:
-
searchOriginをcontainerの閲覧コンテナに設定
-
eventTargetをsearchOriginに設定
-
containerをeventTargetの最も近い祖先で空間ナビゲーションコンテナに設定
-
ループのステップに戻る。
-
-
それ以外は、containerを自身の最も近い祖先で空間ナビゲーションコンテナに設定し、ループに戻る。
-
-
-
bestCandidateを最適な候補の選択を candidates内でdirectionとeventTargetで実行した結果とする。
-
navbeforefocusイベント発火を eventTarget、direction、bestCandidateに対して行う。
-
フォーカス手順をbestCandidateに対して実行し、終了。
8.4. フォーカスナビゲーションのヒューリスティクス
注: 以下のアルゴリズムはChromeの実装や旧WICD仕様から着想を得ています。 より良い手法や改良を見つけた実装者は、フィードバックを提供し本仕様の改善に協力することが強く推奨されます。 特に、ユーザーエージェントごとにフォーカス可能エリアの検索方法が異なると、 ある要素が一部のユーザーエージェントでフォーカス可能だが他ではそうでないという状況が生じ、 これはユーザーにとって望ましくありません。
このセクションの全ての幾何学的操作は、CSSレイアウトの結果上で定義されており、 相対位置指定や [CSS-TRANSFORMS-1]など全てのグラフィック変形を含みます。
検索起点を設定するには、以下の手順を実行します:
-
searchOriginをDOMアンカー(トップレベル閲覧コンテキストの現在フォーカスされている領域)の値とする。
-
spatial navigation starting pointが
null
でなく、 searchOrigin内にある場合は、それを返す。 -
それ以外の場合はsearchOriginを返す。
フォーカスターゲットの状態がフォーカス移動以外で変化した場合、検索起点を更新する手順:
-
focus targetが実際に無効または明示的に非活性、もしくは レンダリングされていない場合は、 searchOriginをfocus targetのboundary boxとする。
-
focus targetが削除された場合は、 searchOriginをfocus targetが存在していた位置のboundary boxとする。
-
focus targetが完全に画面外の場合は、 searchOriginをfocus targetの最も近い表示中の空間ナビゲーションコンテナのビューポートとする。
注: ユーザーエージェントは、例えば フォーカス要素がマウススクロールによって画面外に移動したり、ビューポートから消えた時などに 検索起点を更新すべきです。
フォーカス可能エリアの検索:要素C内で
オプションのvisibleOnly引数(デフォルトtrue
)を伴って以下の手順を実行します。
-
focusablesを、Cの子孫でfocusable areaのDOMアンカーを持つもの全ての集合とする。 ボックスが複数のボックスフラグメントを持つ場合は、 各ボックスフラグメントを個別に扱う。
-
ユーザーエージェントは、focusables集合から削除すべき項目は、そのDOMアンカーの
tabindex
属性が負値の場合。注: これは、tabindexで定義される 逐次フォーカスナビゲーション順から 負のtabindex要素を除外することを反映しています。
-
visibleOnlyが
false
なら、focusablesを返す。注: focusablesは空の場合もある
-
visiblesを、focusablesのうちboundary boxが Cのinside area内に少しでも入っている項目の部分集合とする。
現在スクロール不可部分にある要素以外は、 空間ナビゲーションはクリックできない要素(他の要素で覆われている等)を 自動的に除外しません。 アプリロジックの前提を壊さないためや、 ユーザーが見えない・到達不可な要素にフォーカスして混乱しないために、 著者は逐次ナビゲーションと同じベストプラクティス(tab-index="-1"やinert
属性利用など)で 空間ナビゲーションからも除外すべきです。 -
visiblesを返す。
注: visiblesは空の場合もある
最適な候補の選択:方向dir、searchOriginを起点として candidates集合内で以下の手順を実行します:
-
candidatesが空なら
null
を返す -
candidatesが1つだけならそれを返す
-
insidersをcandidatesの部分集合とする
-
boundary boxがsearchOriginのinside areaと完全に重なっているもの
-
boundary boxが部分的にsearchOriginのinside areaと重なっているもの:
-
dirが
down
なら 上端がsearchOriginのboundary boxの上端より下 -
dirが
up
なら 下端がsearchOriginのboundary boxの下端より上 -
dirが
left
なら 右端がsearchOriginのboundary boxの右端より左 -
dirが
right
なら 左端がsearchOriginのboundary boxの左端より右
注: 要素が検索起点とどのように重なっているかの詳細条件は フォーカス移動の順序に影響します。順序はUXに関係するため、UA定義の仕組みに依存します。
-
注: この部分集合化は要求された方向と逆に進むことを避けるために必要です。
-
-
-
insidersが空でなければ、
-
closest subsetをinsidersの部分集合とし、 boundary boxの
-
dirが
down
なら 上端がsearchOriginのinside areaの上端に最も近いもの -
dirが
up
なら 下端がsearchOriginのinside areaの下端に最も近いもの -
dirが
left
なら 右端がsearchOriginのinside areaの右端に最も近いもの -
dirが
right
なら 左端がsearchOriginのinside areaの左端に最も近いもの
-
-
closest subsetが1つならそれを返す。それ以外はドキュメント順の最初の要素を返す。ただしそのboundary boxが他の要素のboundary boxと重なり、 その要素がCSS描画順で上なら、その要素を返す。これも上位要素と重なる場合は再帰的に適用。
-
-
それ以外の場合
-
candidatesを次の条件を満たす部分集合とする:
-
要素がsearchOriginと重ならず、そのboundary boxが
-
dirが
down
なら、上端がsearchOriginのboundary boxの下端より下 -
dirが
up
なら、下端がsearchOriginのboundary boxの上端より上 -
dirが
left
なら、右端がsearchOriginのboundary boxの左端より左 -
dirが
right
なら、左端がsearchOriginのboundary boxの右端より右
-
-
-
candidates集合内の各candidateについて 最短距離を算出する。
-
距離が最小の要素を返す。同距離ならドキュメント順の最初の要素。ただしそのboundary boxが他の要素のboundary boxと重なり、 その要素がCSS描画順で上なら、その要素を返す。これも上位要素と重なる場合は再帰的に適用。
-
-
distance = euclidean + displacement - alignment - sqrt(Overlap)
各項目の意味:
- euclidean
- P1とP2間のユークリッド距離
- displacement
-
referenceとcandidate間でdirに対する変位量。定義:
displacement = (絶対値で直交軸方向の距離 + orthogonalBias) * orthogonalWeight
- orthogonalBias:
- orthogonalWeight:
- alignment
-
referenceとcandidateのdir方向の整列度。定義:
alignment = alignBias * alignWeight
- alignBias:
- projectedOverlap:
-
projectedOverlap - alignWeight:
- 5
- sqrt(Overlap)
- referenceとcandidateの重なり面積の平方根(重ならなければ0)
注: この一般式は複数の候補式からUXテストケースで直感に最も合うものを選びました。 alignWeightやorthogonalWeightの値も同様に実験的に決定しています。 結果として複雑な式ですが、良好な結果が得られるようです。 改善や単純化の提案も歓迎します。
9. 宣言的手法による空間ナビゲーションの制御
9.1. 追加の空間ナビゲーションコンテナの作成: spatial-navigation-contain プロパティ
名前: | spatial-navigation-contain |
---|---|
値: | auto | contain |
初期値: | auto |
適用対象: | すべての要素 |
継承: | no |
パーセンテージ: | n/a |
算出値: | 指定通り |
正規順序: | 文法順 |
アニメーション型: | 離散 |
- auto
- 要素がスクロールコンテナの場合、空間ナビゲーションコンテナを確立します。 それ以外の場合は確立しません。
- contain
- この要素は空間ナビゲーションコンテナを確立します。
注: さらに § 8.2 要素のグループ化 に従い、 閲覧コンテキストのビューポート(トップレベル閲覧コンテキストに限定されません) も空間ナビゲーションコンテナを確立します。
この場合、グリッドがかなりまばらなため、 ユーザーが「Foo」から下方向に移動しようとすると、 フォーカスは「Next Week」に移動します。 これは下方向で客観的に最も近い位置にあるためです。 「Bar」から下方向に移動した場合も同様に、 フォーカスは「Previous Week」に移動します。
< div >
< button > 前週</ button >
< table >
< tr >< td >< th > M< th > T< th > W< th > T< th > F< th > S< th > S
< tr >< td > 0-6< td >< td >< td >< td >< td >< td >< td >< a href = "#" > Foo</ a >
< tr >< td > 6-9< td >< a href = "#" > Bar</ a >< td >< td >< td >< td >< td >< td >
< tr >< td > 9-12< td >< td >< a href = "#" > Bat</ a >< td >< td >< td >< td >< td >
< tr >< td > 12-18< td >< td >< td >< td >< td >< td >< td >
< tr >< td > 18-21< td >< td >< td >< td >< td >< td >< td >< a href = "#" > Woo</ a >
< tr >< td > 21-24< td >< td >< td >< td >< td >< td >< a href = "#" > Baz</ a >< td >
</ table >
< button > 次週</ button >
</ div >
しかし、著者は異なるナビゲーション体験を提供したい場合もあります。 たとえば、グリッド内のどれかにフォーカスした後は、 テーブル内の動きを優先させたい場合です。 これはテーブル内の要素同士が意味的に関連しているためです。
スタイルシートに
を追加すると、このような挙動となります。
この後、「Foo」から下方向にフォーカスを移動すると「Woo」に、 「Bar」から下方向では「Bat」に移動します。 「Next Week」や「Previous Week」には移動しません。
それでもテーブル外にフォーカスを移動することは可能です。 たとえば、「Foo」から右に移動するとグリッド右側に要素がないため「Next Week」にフォーカスが移動します。
注: spatial-navigation-containプロパティはリスクありです。
9.2. スクロールとの連携制御: spatial-navigation-action プロパティ
名前: | spatial-navigation-action |
---|---|
値: | auto | focus | scroll |
初期値: | auto |
適用対象: | スクロールコンテナ |
継承: | no |
パーセンテージ: | n/a |
算出値: | 指定通り |
正規順序: | 文法順 |
アニメーション型: | 離散 |
フォーカスがスクロールコンテナ内にある状態でユーザーが空間ナビゲーションをトリガーすると、 フォーカスをその方向に移動したいのか、 ドキュメント自体をその方向にスクロールしたいのかが曖昧になる場合があります。 デフォルトでは自動的に判定されますが、このプロパティを使うことで、 フォーカス移動とスクロールのどちらを優先するかを著者が指定できます。
厳密な挙動は § 8.3 ナビゲーション で定義されていますが、 各値の効果を高いレベルで説明します。
空間ナビゲーションがトリガーされると、 現在フォーカスされている要素がspatial-navigation-actionを持ち、 その要素がスクロールコンテナの場合はその値が、 要素がそうでない場合は最も近いスクロールコンテナ祖先の値が使われます。
- auto
- 指定方向にスクロールコンテナ内に 表示されているフォーカス可能な要素があれば、一番近いものにフォーカスが移動します。 なければスクロールコンテナが指定方向にスクロールされます。
- focus
-
表示状態に関係なく、スクロールコンテナ内で
一番近いフォーカス可能要素にフォーカスが移動します。
要素がなければ、
スクロールコンテナはスクロールされません。
代わりに祖先方向の探索が続行されます。
注: スクロールコンテナは、 もともと表示されていなかった要素がフォーカスされることで副次的にスクロールされることがありますが、 方向スクロールは実行されません。
注: focus値を spatial-navigation-actionに指定した場合、 ビューポート内に表示されている候補がない方向で navnotargetイベントが発火します。 この場合は、コンテナがさらにスクロール可能でも発火します。
- scroll
-
現在フォーカスされている要素がスクロールコンテナでない場合は、
祖先スクロールコンテナのこの値はautoと同じ動作になります。
現在フォーカスされている要素がスクロールコンテナの場合は、 フォーカス可能な子孫の有無に関係なく、フォーカスは変わらず、指定方向にスクロールのみ実行されます。
注: この値では空間ナビゲーションでスクロールコンテナにフォーカスを移動し、スクロールすることはできますが、 子孫要素へのフォーカス移動はできません。 ただし、Tabキーや <
focus()
> メソッドなど 他の手段で子孫にフォーカスを移した後は、 空間ナビゲーションで他の子孫要素にフォーカス移動できます。注: scroll値はリスクありです。
注: 以前の仕様ではfocus動作への宣言的な方法がなく、 スクロール前に発火するキャンセル可能なイベントを提供し、 著者自身でその動作を実装できるようにしていました。 しかし、スクロール関連のキャンセル可能イベントはパフォーマンス問題につながるため、 このイベントは削除され、代わりにspatial-navigation-actionプロパティが導入されました。
spatial-navigation-action: focus
を指定した
スクロール可能なコンテナがあります。
コンテナ内にはscrollport内の表示領域外にある要素があります。
下矢印キーを押すと、手動スクロールせずに直接その要素にフォーカスが移動します。

< div class = 'scroller' >
< button class = 'item' > Box 1</ button >
< button class = 'item' > Box 2</ button >
< button class = 'item' > Box 3</ button >
</ div >
.scroller {
display : grid;
grid-template-columns : repeat ( 1 , 1 fr );
height : 300 px ;
width : 200 px ;
overflow-y : scroll;
spatial-navigation-action : focus;
}
.item {
height : 100 px ;
width : 100 px ;
margin : 50 px auto;
background-color : blue;
}
:focus {
background-color : red;
}
9.3. ナビゲーションアルゴリズムの選択: spatial-navigation-function プロパティ
名前: | spatial-navigation-function |
---|---|
値: | normal | grid |
初期値: | normal |
適用対象: | 空間ナビゲーションコンテナ |
継承: | no |
パーセンテージ: | n/a |
算出値: | 指定通り |
正規順序: | 文法順 |
アニメーション型: | 離散 |
§ 8 処理モデルで指定されている空間ナビゲーションのデフォルトアルゴリズムは、レイアウトタイプによって微調整が必要な場合があります。 このプロパティは、著者が空間ナビゲーション動作に対して最適なナビゲーションアルゴリズムを指定できます。
値の定義は以下の通りです。
- normal
-
UAが定義したデフォルトのフォーカスナビゲーションアルゴリズムでフォーカスを移動します。
一般的には、最短距離の算出で計算された距離が最も近い要素にフォーカスが移動します。
- grid
-
ナビゲーション方向に最も揃っている要素にフォーカスが移動します。
-
ナビゲーション方向に揃っている候補が複数ある場合、 ナビゲーション方向の軸に沿って距離が最も近い要素を選択します。 同じ距離の要素が複数ある場合は、揃い度が最も小さいものを選びます。
-
指定方向に揃っている候補がない場合は、 ナビゲーション方向の軸に沿って距離が最も近い要素を選択します。 同じ距離の要素が複数ある場合は、ナビゲーション方向と直交する軸の距離が最も小さいものを選びます。
-
注: これらの値は、ユーザーの好みと協調され、各ページで自然な空間ナビゲーション動作になるようにしています。
spatial-navigation-function
値によって
フォーカスの移動がどのように違ってくるかを示しています。

「A」「B」「C」を含む要素が空間ナビゲーションコンテナであるとします。
ユーザーが下矢印キーを押した場合、
要素のspatial-navigation-function
値がnormal
ならフォーカスは「B」に移動します。
逆にgrid
が指定されていれば、フォーカスは「C」に移動します。
付録A. スクロール拡張
このセクションでは、CSSへのいくつかの拡張案を提案します。 これらは最終的には公式仕様に統合されるべきですが、それまで暫定的にここで定義しています。
このような用語は [CSSOM-VIEW-1]、[CSS-OVERFLOW-3]、[CSS-SCROLL-SNAP-1] に含めるべきです。 <https://github.com/w3c/csswg-drafts/issues/2322>
要素 e は、指定された方向 d において 手動スクロール可能 です、条件は以下の通りです:
-
e が確立する principal box が スクロールコンテナであること
-
d が
up
またはdown
の場合、 overflow-y プロパティの算出値が ではないこと -
d が
left
またはright
の場合、 overflow-x プロパティの算出値が ではないこと -
e が スクロール境界の方向 d に位置していないこと
-
e が方向 d の最後の mandatory スナップポイントにスナップされていないこと
[CSSOM-VIEW-1] で明示的な位置指定なしで方向スクロールの方法を定義すべきです。 それまで、暫定的に独自手法で対応します。 <https://github.com/w3c/csswg-drafts/issues/2323>
要素の方向スクロール e を方向 dir へ行う場合:
-
d をユーザーエージェント定義の距離とする。
-
x を e の x軸の現在のスクロール位置とする。
-
y を e の y軸の現在のスクロール位置とする。
-
e に対し scroll an element アルゴリズムを用いて
-
dir が
up
の場合 (x, y - d) -
dir が
down
の場合 (x, y + d) -
dir が
left
の場合 (x - d, y) -
dir が
right
の場合 (x + d, y)
-
付録B. プライバシーとセキュリティに関する考察
この仕様の編集者は、 仕様に関連する既知の全ての潜在的なセキュリティリスクが十分に対策されていると考えています。 詳細は下記に記載します。
TAGは自己レビュー質問票を作成しており、 編集者やワーキンググループが仕様によるリスクを評価できるようになっています。 以下に回答します。
- この仕様は個人識別情報を扱いますか?
- いいえ。
- この仕様は高価値データを扱いますか?
- いいえ。
- この仕様はOriginごとにブラウジングセッションをまたいで保持される新しい状態を導入しますか?
- いいえ。
- この仕様はWebに永続的かつクロスオリジンな状態を公開しますか?
- いいえ。
- この仕様はOriginに現在アクセスできない他のデータを公開しますか?
-
基本的にいいえ。
唯一の例外は次のようなシナリオです: 著者が `window.navigate` を使い、フォーカスがクロスオリジンiframe内にある場合、 もしイベントを受け取れなかった場合は、iframe内にスクロール可能またはフォーカス可能なものがあったことを意味します。 なぜなら、イベントが発火される唯一のケースは、何も見つからずツリーを上がった場合のみだからです。
この情報はごく限定的で、実質的なセキュリティリスクにはならないように見えますが、 編集者の知る限りでは著者が他の方法で取得できない情報です。
- この仕様は新しいスクリプト実行やロード機構を可能にしますか?
- いいえ。
- この仕様はOriginにユーザーの位置情報へのアクセスを許可しますか?
- いいえ。
- この仕様はOriginにユーザーの端末センサーへのアクセスを許可しますか?
- いいえ。
- この仕様はOriginにユーザーのローカル環境の側面へのアクセスを許可しますか?
- いいえ。
- この仕様はOriginに他のデバイスへのアクセスを許可しますか?
- いいえ。
- この仕様はOriginにユーザーエージェントのネイティブUIの一部を制御可能にしますか?
- ユーザーエージェントのUI外観に関する制御はありません。 空間ナビゲーションの挙動に関して一部制御が可能です。 これは意図的なものであり、著者が空間ナビゲーションの挙動をページごとに調整できるようにしています。 悪意のある著者がユーザーの意図するフォーカス制御やドキュメントナビゲーションを妨害しないよう、 この上書き機構はクロスオリジンiframeについてはデフォルトで無効です。 § 7 navigation-override ポリシー制御機能参照。
- この仕様はWebに一時的な識別子を公開しますか?
- いいえ。
- この仕様はファーストパーティとサードパーティ文脈で動作を区別しますか?
- いいえ。
- この仕様はユーザーエージェントの「シークレット」モードでどのように動作すべきですか?
- 違いはありません。
- この仕様はユーザーのローカル端末にデータを永続化しますか?
- いいえ。
- この仕様は「セキュリティに関する考察」と「プライバシーに関する考察」セクションを持っていますか?
- はい。現在読んでいるこのセクションです。
- この仕様はデフォルトのセキュリティ特性をダウングレードすることを許可しますか?
-
関連しないセキュリティ機構のダウングレードは許可されません。
ただし、著者が信頼するクロスオリジンiframeで空間ナビゲーションのデフォルト挙動を上書きするための 必要なイベントを許可することは可能です。 [feature-policy]参照。 § 7 navigation-override ポリシー制御機能参照。
謝辞
この仕様の編集者は、次の方々にフィードバックと貢献(アルファベット順)を感謝します:
-
Alice Boxhall
-
Brian Kardell
-
Elika Etemad
-
Eric Seong
-
Hugo Holgersson
-
Hyojin Song
-
Jeonghee Ahn
-
Junho Seo
-
Rob Dodson
-
Seungcheon Baek
変更履歴
このセクションは規範的ではありません。
以下は、2019年4月23日 初回公開ワーキングドラフト以降の変更点です。
-
getSpatialNavigationContainer()
の結果を、最も近い空間ナビゲーションコンテナ祖先を返すよう変更 -
検索起点の更新手順を追加
-
spatialNavigationSearch()
のIDLを、dir属性をSpatialNavigationSearchOptions
から分離する形で変更 -
検索起点と完全に重なるフォーカス可能要素(空間ナビゲーションコンテナではないもの)を候補とすることで到達不可問題への対策を追加