Skip to content

Commit 46cc66e

Browse files
committed
Remove using the enter key to navigate inside scrollers
Instead, navigating down (resp. up, left, right) when a scroller is focused will automatically chose between * focusing the child nearest from the edge in that direction if there is any focusable child * scrolling otherwise This also removes much of the special casing needed for the root. Some ambiguities / inacuracies were resolved along the way. Closes w3c#15 Closes w3c#18 Partly relates to w3c#29
1 parent 5d119b9 commit 46cc66e

File tree

1 file changed

+110
-115
lines changed

1 file changed

+110
-115
lines changed

index.bs

Lines changed: 110 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -171,27 +171,6 @@ until it has either moved focus,
171171
scrolled,
172172
or reached the root.
173173

174-
Additionally, when the user has focused a <a>scroll container</a> which contains focusable elements,
175-
the user may ask the User Agent to move the focus to the nested elements
176-
(for instance by pressing <code class=key>Enter</code>).
177-
178-
The User Agent will then follow a similar logic: first, search for visible and focusable items
179-
within the currently focused <a>scroll container</a>,
180-
and if there is any,
181-
select the best one and move the focus there.
182-
183-
<div class=note>Note: This feature is needed because elements inside a scrollable container
184-
are neither to its top, bottom, left or right.
185-
They are inside, and could not be reached otherwise using spatial navigation only.
186-
187-
Issue(15): While this ability is needed, it could be achieved by different mechanisms,
188-
such as for example automatically moving the focus inside of a scroll container
189-
instead of focusing it,
190-
if it contains focusable elements.
191-
The current proposition is felt to be more intuitive for end users.
192-
Feedback appreciated.
193-
</div>
194-
195174
At key points during this search for the appropriate response to the spatial navigation request,
196175
the User Agent will fires events.
197176
These enable authors to prevent the upcoming action
@@ -375,7 +354,6 @@ enum SpatialNavigationDirection {
375354
"down",
376355
"left",
377356
"right",
378-
"inside"
379357
};
380358

381359
enum FocusableAreaSearchMode {
@@ -409,7 +387,9 @@ Note: the {{focusableAreas()}} and {{getSpatnavContainer()}} methods are <a>at-r
409387

410388
<div algorithm="getSpatnavContainer steps">
411389
The {{Element/getSpatnavContainer()}} method must follow these steps:
412-
1. Return the nearest ancestor of the element that is a <a>spatnav container</a>
390+
1. Return the element if it is a <a>spatnav container</a>,
391+
or the nearest ancestor of the element that is a <a>spatnav container</a>,
392+
or the <a>document</a> if the nearest <a>spatnav container</a> is the viewport.
413393

414394
</div>
415395

@@ -462,7 +442,7 @@ The {{Element/seqNavSearch()}} method must follow these steps:
462442

463443
<pre><code highlight=javascript>
464444
document.addEventListener("navbeforescroll", function(e) {
465-
var container = e.target.getSpatnavContainer();
445+
var container = e.relatedTarget;
466446
var areas = container.focusableAreas({ mode: "all" });
467447

468448
if (areas.length == 0)) { return; }
@@ -495,7 +475,6 @@ enum NavigationDirection {
495475
"down",
496476
"left",
497477
"right",
498-
"inside",
499478
"forward",
500479
"backward"
501480
};
@@ -874,11 +853,10 @@ if a suitable one cannot be found inside it (see [[#nav]] for details).
874853
Such groupings are called <dfn lt="spatial navigation focus container | spatial navigation focus containers | spatnav container | spatnav containers">spatial navigation focus containers</dfn> (or <strong>spatnav containers</strong> for short).
875854

876855
By default, <a>spatnav containers</a> are established by:
877-
* the <a>document element</a> of a <a for="/">browsing context</a>'s <a>document</a>
856+
* The viewport of a <a for="/">browsing context</a>
878857
(not limited to the <a>top-level browsing context</a>)
879858

880-
Issue(18): Should that be the viewport rather than the document element?
881-
* a <a>scroll containers</a>
859+
* <a>scroll containers</a>
882860

883861
Additional <a>spatnav containers</a> can be created using the 'spatial-navigation-contain' property (see [[#container]]).
884862

@@ -899,27 +877,40 @@ Issue(23): The focusing steps should probably reset the <a>spatial navigation st
899877
To run the <dfn>spatial navigation steps</dfn> in <var>direction</var>, do the following:
900878

901879
1. Let <var>startingPoint</var> be the <a>DOM anchor</a> of the <a>currently focused area of a top-level browsing context</a>.
902-
2. If <var>startingPoint</var> is the <a>Document</a> of the <a>top-level browsing context</a>
903-
set <var>startingPoint</var> to <a>the body element</a> if it is not <code>null</code>
904-
or to the <a>document element</a> otherwise.
905-
3. If the <a>spatial navigation starting point</a> is not <code>null</code>
906-
and it is a descendant of <var>startingPoint</var>
880+
2. If the <a>spatial navigation starting point</a> is not <code>null</code>
881+
and it is inside <var>startingPoint</var>
907882
then set <var>startingPoint</var> to the <a>spatial navigation starting point</a>
908-
4. If the <var>direction</var> is <code>inside</code>,
909-
then run the <a>navigate inside steps</a> on <var>startingPoint</var>.
910-
1. Let <var>eventTarget</var> be <var>startingPoint</var> if <var>startingPoint</var> is an element,
911-
or let <var>eventTarget</var> be the element which contains <var>startingPoint</var>
912-
if <var>startingPoint</var> is a position.
913-
(<a>assert</a>: There is no other alternative)
914-
2. If <var>starting point</var> is the <a>document element</a> or the <a>the body element</a> of the <a>top-level browsing context</a>
915-
then set <var>starting point</var> to:
916-
* the top edge of the viewport if <var>direction</var> is <code>down</code>
917-
* the bottom edge of the viewport if <var>direction</var> is <code>up</code>
918-
* the left edge of the viewport if <var>direction</var> is <code>right</code>
919-
* the right edge of the viewport if <var>direction</var> is <code>left</code>
920-
921-
Note: We special case the situation where we're navigating from the state where nothing was focused,
922-
to start searching from the edges of the viewport.
883+
4.
884+
* If <var>startingPoint</var> is an <a>node</a>,
885+
let <var>eventTarget</var> be <var>startingPoint</var>
886+
* else (<a>assert</a>: the starting point is a position)
887+
let <var>eventTarget</var> be the <a>node</a> which contains <var>startingPoint</var>
888+
5. If <var>eventTarget</var> is the <a>Document</a> or the <a>document element</a>,
889+
set <var>eventTarget</var> be <a>the body element</a> if it is not <code>null</code>
890+
or to the <a>document element</a> otherwise.
891+
5. If <var>startingPoint</var> is either a <a>scroll container</a> or the <a>document</a>
892+
1. Let <var>candidates</var> be the result of <a>finding focusable areas</a>
893+
within <var>startingPoint</var>
894+
2.
895+
* If <var>candidates</var> contains at least 1 item
896+
1. Let <var>bestCandidate</var> be the result of <a>selecting the best candidate</a>
897+
within <var>candidates</var> in <var>direction</var> starting from <var>startingPoint</var>
898+
2. <a>Fire an event</a> named <a event>navbeforefocus</a> at <var>eventTarget</var> using {{NavigationEvent}}
899+
with its {{NavigationEvent/dir}} set to <var>direction</var> and {{NavigationEvent/relatedTarget}} set to <var>bestCandidate</var>
900+
and with it's <code>bubbles</code> and <code>cancelable</code> attributes set to <code>true</code>,
901+
and let <var>allowFocusChange</var> be the result.
902+
3. If <var>allowFocusChange</var> is <code>false</code>, return
903+
4. Run the <a>focusing steps</a> for <var>bestCandidate</var> and return
904+
* Else if <var>eventTarget</var> <a>can be manually scrolled</a>
905+
1. <a>Fire an event</a> named <a event>navbeforescroll</a> at <var>eventTarget</var> using {{NavigationEvent}}
906+
with its {{NavigationEvent/dir}} set to <var>direction</var>
907+
and {{NavigationEvent/relatedTarget}} set to <var>eventTarget</var>
908+
and with it's <code>bubbles</code> and <code>cancelable</code> attributes set to <code>true</code>,
909+
and let <var>allowScroll</var> be the result.
910+
2. If <var>allowScroll</var> is <code>true</code>,
911+
then <a>Directionally scroll the element</a> <var>eventTarget</var> in <var>direction</var> and return,
912+
else return.
913+
* Else, fallback to the next step
923914
3. Let <var>container</var> be the nearest ancestor of <var>eventTarget</var> that is a <a>spatnav container</a>.
924915
4. <i>Loop</i>: Let <var>candidates</var> be the result of <a>finding focusable areas</a>
925916
within <var>container</var>
@@ -931,7 +922,7 @@ To run the <dfn>spatial navigation steps</dfn> in <var>direction</var>, do the f
931922
and with it's <code>bubbles</code> and <code>cancelable</code> attributes set to <code>true</code>,
932923
and let <var>allowScroll</var> be the result.
933924
2. If <var>allowScroll</var> is <code>true</code>,
934-
then return <a>Directionally scroll the element</a> <var>container</var> in <var>direction</var> the return,
925+
then <a>Directionally scroll the element</a> <var>container</var> in <var>direction</var> and return,
935926
else return.
936927
* Else,
937928
1. <a>Fire an event</a> named <a event>navnotarget</a> at <var>eventTarget</var> using {{NavigationEvent}}
@@ -962,27 +953,6 @@ To run the <dfn>spatial navigation steps</dfn> in <var>direction</var>, do the f
962953

963954
</div>
964955

965-
<div algorithm="to run the navigate inside steps">
966-
To run the <dfn>navigate inside steps</dfn> on <var>eventTarget</var>, do the following:
967-
1. Let <var>candidates</var> be the result of <a>finding focusable areas</a>
968-
within <var>eventTarget</var>
969-
2. If <var>candidates</var> is <code>null</code>,
970-
<a>Fire an event</a> named <a event>navnotarget</a> at <var>eventTarget</var> using {{NavigationEvent}}
971-
with its {{NavigationEvent/dir}} set to <code>inside</code> and {{NavigationEvent/relatedTarget}} set to <var>eventTarget</var>
972-
and with it's <code>bubbles</code> and <code>cancelable</code> attributes set to <code>true</code>,
973-
then return.
974-
3. Let <var>bestCandidate</var> be the result of <a>selecting the best candidate</a>
975-
within <var>candidates</var> in direction <code>inside</code>
976-
starting from the <a>inline start</a> <a>block start</a> corner of <var>eventTarget</var>'s <a>scrollport</a>.
977-
4. <a>Fire an event</a> named <a event>navbeforefocus</a> at <var>eventTarget</var> using {{NavigationEvent}}
978-
with its {{NavigationEvent/dir}} set to <code>inside</code> and {{NavigationEvent/relatedTarget}} set to <var>bestCandidate</var>
979-
and with it's <code>bubbles</code> and <code>cancelable</code> attributes set to <code>true</code>,
980-
and let <var>allowFocusChange</var> be the result.
981-
5. If <var>allowFocusChange</var> is <code>false</code>, return
982-
6. Run the <a>focusing steps</a> for <var>bestCandidate</var> and return
983-
984-
</div>
985-
986956
For consistency between <a>sequential focus navigation</a> and the model defined above,
987957
the following is also defined:
988958

@@ -1018,10 +988,9 @@ All geometrical operations in this section are defined to work on the result of
1018988
including all graphical transformations, such as <a>relative positioning</a> or [[CSS-TRANSFORMS-1]].
1019989

1020990
The <dfn>boundary box</dfn> of an object is defined as follows:
1021-
* if the object is a point, the boundary is that point
1022-
* if the object is an element, the boundary is the <a>border box</a> of the element's <a>principal box</a>.
1023-
* if the object is a <a>focusable area</a> which is not an element, the boundary is the axis-aligned the bounding box of that <a>focusable area</a>
1024-
* if the object is a geometric shape, the boundary is the axis-aligned the bounding box of that shape
991+
* if the object is a point, the boundary box is that point
992+
* if the object is an element, the boundary box is the <a>border box</a> of the element's <a>principal box</a>.
993+
* if the object is a <a>focusable area</a> which is not an element, the boundary box is the axis-aligned the bounding box of that <a>focusable area</a>
1025994

1026995
Issue(w3c/csswg-drafts#2324): CSS should have a term for “border box taking into account corner shaping properties like border-radius”.
1027996

@@ -1040,12 +1009,16 @@ follow the following steps:
10401009
return <var>focusables</var>.
10411010

10421011
Note: <var>focusables</var> may be empty
1012+
3. Let <var>insideArea</var> be
1013+
* the <a>optimal viewing region</a> of <var>C</var> if <var>C</var> is a <a>scroll container</a>,
1014+
1015+
Issue(29): Should that be C's <a>optimal viewing region</a> or <a>scrollport</a>?
1016+
1017+
* the viewport of C's <a for="/">browsing context</a> if <var>C</var> is a <a>Document</a>,
1018+
* the <a>border box</a> of <var>C</var> otherwise.
10431019
3. Let <var>visibles</var> be the subset of items in <var>focusables</var>
10441020
whose <a>boundary box</a>
1045-
is at least partly within <var>C</var>'s <a>scrollport</a>.
1046-
1047-
Issue(29): Should that be C's <a>optimal viewing region</a> instead?
1048-
Probably not, given the next step, but maybe.
1021+
is at least partly within <var>insideArea</var>.
10491022
4. Remove from <var>visibles</var> items are <a>obscured</a> by other parts of the page:
10501023
If no point of the area enclosed by an item's <a>boundary box</a> can be hit by a hit test due to some other object(s) overlapping it,
10511024
it is said to be <dfn>obscured</dfn>.
@@ -1080,36 +1053,58 @@ follow the following steps:
10801053

10811054
1. If <var>candidates</var> is <a spec=infra for=set>empty</a>, return <code>null</code>
10821055
2. If <var>candidates</var> contains a single item, return that item
1083-
3. If <var>dir</var> is not <code>inside</code>,
1084-
set <var>candidates</var> be the subset of its items
1085-
whose <a>boundary box</a>'s geometric center is within the closed half plane
1086-
whose boundary goes through the geometric center of the <var>starting point</var>
1087-
and is perpendicular to <var>D</var>.
1088-
3. For each <var>candidate</var> in <var>candidates</var>,
1089-
find the points <var>P1</var> inside the <a>boundary box</a> of <var>starting point</var>
1090-
and <var>P2</var> inside the <a>boundary box</a> of <var>candidate</var>
1091-
that minimize the <var>distance</var> between these two points,
1092-
when <var>distance</var> is defined as follows:
1093-
1094-
<dl>
1095-
<dt><var>distance</var>:
1096-
<dd><var>A</var> + <var>B</var> + <var>C</var> - <var>D</var>
1097-
1098-
<dt><var>A</var>:
1099-
<dd>The euclidian distance between <var>P1</var> and <var>P2</var>.
1100-
1101-
<dt><var>B</var>:
1102-
<dd>The absolute distance in the <var>dir</var> direction between <var>P1</var> and <var>P2</var>,
1103-
or 0 if <var>dir</var> is <code>inside</code>.
1104-
1105-
<dt><var>C</var>:
1106-
<dd>The absolute distance in the direction which is orthogonal to <var>dir</var> between <var>P1</var> and <var>P2</var>,
1107-
or 0 if <var>dir</var> is <code>inside</code>.
1108-
1109-
<dt><var>D</var>:
1110-
<dd>The square root of the area of intersection between the <a>boundary boxes</a> of <var>candidate</var> and <var>starting point</var>
1111-
</dl>
1112-
4. Return the item of the <var>candidates</var> set that has the smallest <var>distance</var>
1056+
3. Let <var>insideArea</var> be
1057+
* the <a>optimal viewing region</a> of <var>starting point</var> if <var>starting point</var> is a <a>scroll container</a>,
1058+
* the viewport if <var>starting point</var> is a <a>Document</a>,
1059+
* the <a>border box</a> of <var>starting point</var> otherwise.
1060+
4. Let <var>insiders</var> be the subset of <var>candidates</var> items
1061+
who are descendants of <var>starting point</var>
1062+
and whose <a>boundary box</a>'s
1063+
* top edge is below the top edge of <var>insideArea</var> if <var>D</var> is <code>down</code>
1064+
* bottom edge is above the bottom edge of <var>insideArea</var> if <var>D</var> is <code>up</code>
1065+
* right edge is left of the right edge of <var>insideArea</var> if <var>D</var> is <code>left</code>
1066+
* left edge is right of the left edge of <var>insideArea</var> if <var>D</var> is <code>right</code>
1067+
1068+
Note: this sub-setting is necessary to avoid going in the opposite direction than the one requested.
1069+
3.
1070+
* If <var>insiders</var> is non empty
1071+
1. Let <var>closest subset</var> be the subset of <var>insiders</var> whose <a>boundary box</a>'s
1072+
* top edge is closest to the top edge of <var>insideArea</var> if <var>D</var> is <code>down</code>
1073+
* bottom edge is closest to the bottom edge of <var>insideArea</var> if <var>D</var> is <code>up</code>
1074+
* right edge is closest to the right edge of <var>insideArea</var> if <var>D</var> is <code>left</code>
1075+
* left edge is closest to the left edge of <var>insideArea</var> if <var>D</var> is <code>right</code>
1076+
2. If <var>closest subset</var> contains a single item,
1077+
return that item,
1078+
else return the first item of <var>closest subset</var> in document order
1079+
* Else
1080+
1. Set <var>candidates</var> be the subset of its items
1081+
whose <a>boundary box</a>'s geometric center is within the closed half plane
1082+
whose boundary goes through the geometric center of the <var>starting point</var>
1083+
and is perpendicular to <var>D</var>.
1084+
2. For each <var>candidate</var> in <var>candidates</var>,
1085+
find the points <var>P1</var> inside the <a>boundary box</a> of <var>starting point</var>
1086+
and <var>P2</var> inside the <a>boundary box</a> of <var>candidate</var>
1087+
that minimize the <var>distance</var> between these two points,
1088+
when <var>distance</var> is defined as follows:
1089+
1090+
<dl>
1091+
<dt><var>distance</var>:
1092+
<dd><var>A</var> + <var>B</var> + <var>C</var> - <var>D</var>
1093+
1094+
<dt><var>A</var>:
1095+
<dd>The euclidian distance between <var>P1</var> and <var>P2</var>
1096+
1097+
<dt><var>B</var>:
1098+
<dd>The absolute distance in the <var>dir</var> direction between <var>P1</var> and <var>P2</var>
1099+
1100+
<dt><var>C</var>:
1101+
<dd>The absolute distance in the direction which is orthogonal to <var>dir</var> between <var>P1</var> and <var>P2</var>
1102+
1103+
<dt><var>D</var>:
1104+
<dd>The square root of the area of intersection between the <a>boundary boxes</a> of <var>candidate</var> and <var>starting point</var>
1105+
</dl>
1106+
3. Return the item of the <var>candidates</var> set that has the smallest <var>distance</var>.
1107+
11131108

11141109
</div>
11151110

@@ -1128,18 +1123,18 @@ Inherited: no
11281123

11291124
<dl dfn-for=spatial-navigation-contain dfn-type=value>
11301125
<dt><dfn>auto</dfn>
1131-
<dd>If the element is either
1132-
the <a>document element</a> of a <a for="/">browsing context</a>'s <a>document</a>
1133-
(not limited to the <a>top-level browsing context</a>)
1134-
or
1135-
a <a>scroll container</a>
1136-
then it establishes a <a>spatial navigation focus container</a>,
1137-
otherwise it does not.
1126+
<dd>If the element is a <a>scroll container</a>
1127+
then it establishes a <a>spatial navigation focus container</a>,
1128+
otherwise it does not.
11381129

11391130
<dt><dfn>contain</dfn>
11401131
<dd>The element establishes a <a>spatial navigation focus container</a>
11411132
</dl>
11421133

1134+
Note: In addition, as per [[#grouping]], the viewport of a <a for="/">browsing context</a>
1135+
(not limited to the <a>top-level browsing context</a>)
1136+
also establishes a <a>spatial navigation focus container</a>.
1137+
11431138
Issue(16): Add an example
11441139

11451140
Note: the 'spatial-navigation-contain' property is <a>at-risk</a>.

0 commit comments

Comments
 (0)