wicg / spatial-navigation Goto Github PK
View Code? Open in Web Editor NEWDirectional focus navigation with arrow keys
Home Page: https://drafts.csswg.org/css-nav-1/
License: Other
Directional focus navigation with arrow keys
Home Page: https://drafts.csswg.org/css-nav-1/
License: Other
DOM Events already have two attributes that are named *target*; currentTarget
and target
.
Let's not add a third one? To me, and maybe to others who are not fluent in JavaScript, this is a bit confusing.
Readers shouldn't need to go check the spec to get an idea of what the code does.
The generic spat-nav heuristic can fail, and manually coding everything with nav-up
/ nav-down
/ nav-right
/ nav-left
can be tedious, so something along the lines of the proposed nav-rule
seems very useful.
However, I think coming up with the correct design for this property will be very difficult. Instead
I think we should provide some JS primitives to let page authors and framework authors come up with their own heuristics or algorithms, and only once some clear patterns emerge, standardize that.
I think that all we'd need would be a way for the author to register a JS function that would be called back when the user attempts to navigate directionally, letting javascript figure out which element the focus should go to, and call focus()
on it (and preventDefault()
).
To avoid the tedium of having to set this individually on each and every element the user may need to navigate away from, this should be possible to register on any element in order to get a callback when the user tries to navigate away from any focusable element within that subtree.
The property names and API names used in the spec are probably OK, as they described what they do reasonably well, but I would not go as far as calling them nice.
Better suggestions are very welcome.
In particular, spatnav-container
is a bit questionable. Maybe we should not use the abbreviation, but if we called it spatial-navigation-container
it would look like it may be a longhand of spatial-navigation
, which it is not. Or maybe it is spatial-navigation
that should be renamed into spatnav-something
...
The event names (navnotarget
navbeforefocus
navbeforescroll
) aren't too pretty either: the convention that event names should be all lower case without hyphens or underscrores does limit how nice they can be though.
The JS APIs are pretty descriptive and tied to the name of algorithms in the processing model, but they are quite a mouthful.: findSpatNavCandidates()
selectSpatNavBestCandidate()
sequentialNavSearch()
. Are we happy with that?
webDriver should be extended to be able to invoke spatnav (and sequential nav)
There is another possibility to enable the spatial navigation, using CSS at-rule.
Such as @navigation
, it lets you define the way to move the focus in 4 directions (up/down/left/right).
@navigation
can have a set of descriptors like:
mode: always | common | only
target: <id> | <class> | root
keys: <string>
(or something means the keycode)
For example,
@navigation myNavigation{
mode: common;
keys: 0x26 0x28 0x25 0x27;
}
#sub_root {
nav-rule: myNavigation;
}
This means that the spatial focus navigation works, but controlling UI takes the higher priority than that, and the input is arrow keys.
The JS API for finding the next focusable element would be useful.
element.nextFocusableElement(optional focusNavigationOptions options)
It returns the next focusable element or the list of the focusable elements.
By using it, we can predict the focus event and prepare for it.
It can be used with the focus looping, for example, disabling the focus looping if there isn't the next focusable element.
The IDL would be:
element.nextFocusableElement(optional focusNavigationOptions options)
dictionary focusNavigationOptions {
FocusNavigationRule rule = "nearest";
NavigationDirection direction; // Is this necessary? If it does, what will be the default?
}
enum FocusNavigationRule { "direction", "projection", "nearest" }
enum NavigationDirection { "up", "down", "left", "right" }
direction
is omitted, the API returns the list of focusable element in 4 directions.Similarly to issue #4, it seems to me that arrow-key-behavior
would be simpler and just as useful as an at-rule instead of a property.
In the current spec, there is navigate() which makes possible to trigger spatial navigation by specifying the value for direction, not by pressing the arrow keys.
I think the main purpose of this function is for testing.
In the near future, using arrow keys in the automatic test mechanism may be supported.
So if this function doesn't have other critical usages, we need to consider keep this in the spec or not.
I think spatial-navigation
CSS property in the spec is needed for
Guarantees the author to provide the page with spatial navigation.
Activate spatial navigation to the part of the document.
For example, if there is a document with too many focusable elements, some people may don't want to navigate all of those with the arrow keys.
Therefore, the author can specify spatial-navigation: active
to specific elements which he/she really wants to navigate.
But do we really need this feature in the spec?
Can we let User Agent control the activation of spatial navigation?
I am not a security person. Is the way spatnav and its various Events and APIs work with iframes sane? In particular, https://wicg.github.io/spatial-navigation/#spatial-navigation-steps should be reviewed.
To prevent hostile third party iframes (e.g. ads) from hijacking and trapping the focus, the spatpav events (and possibly APIs, althouh I'm less sure about that) should not be available by default in cross origin iframes.
The top-level document should have a way of turning them back on for iframes it trusts.
This example from the FAQ is probably a good starting point:
https://github.com/WICG/spatial-navigation/blob/master/explainer.md#why-do-we-need-a-focus-container-property-is-it-not-enough-to-placing-the-focusables-next-to-each-other-to-create-a-group-what-use-cases-do-you-see-for-focus-container
Remark about this part: https://github.com/WICG/spatial-navigation/blob/master/explainer.md#nav-rule-property-cssui4
The algorithm for determining how the implementation (or I guess, in this case a reference implementation polyfill) should find the next target in a given direction isn't specified - anything that is not obvious to implement (e.g. nearest) should be specified.
See this guidance from the tag:
https://w3ctag.github.io/design-principles/#always-add-event-handlers
As currently written, the currently focused element is included in the result of find candidates, and not discarded from the list later.
We should either:
The later seems better, as that preserves the ability of find candidates to work as a generic algo to support focusableAreas()
.
I suspect they should, but opening the question for discussion.
Currently, the suggested API could be used like this:
anyelement.findSpatNavCandidates({dir: "left", somecontainerdiv});
I'd like to flip the perspective. Now we expose this method on the element from where we want to "measure reachability" of candidates. I'd like to call this method on the node whose sub tree I want to search. I think that would result in JS that's slightly more pleasant to read.
div.findFocusableAreas()
// Returns all visible focusables within the div.
div.findFocusableAreas("left")
// Visible focusables within the div that are left of document.activeElement().
div.findFocusableAreas("left", startelement)
// Visible focusables in the div that are left of startelement.
Question: What if startelement isn't visible?
I think that reads better than the current draft's: "If c is null, set c to the nearest ancestor of eventTarget that is a spatnav container."
It is not entirely clear to me that having different values of nav-loop
on different parts of the page is useful. If there are indeed use cases that would benefit from it, the then current design (modulo issue #1 ) is fine, but if not, it introduces unnecessary complexity.
It also seems like an easy way to accidentally create designs that would frustrate users: if a DOM sub-tree has nav-loop: repeat
, but there are focusable elements outside of that subtree, the users will not be able to get there. The same thing can already happen with poor usage of nav-up
/ nav-down
/ nav-right
/ nav-left
, but doing so accidentaly seems more likely with nav-loop
as a property.
This would solve the key use case, without having that downside:
@nav-loop: auto | repeat | repeat-x | repeat-y | no-repeat
Alternatively, if we end up with other spat-nav related things that belong in at-rules, we could have:
@spat-nav {
[...]
loop: auto | repeat | repeat-x | repeat-y | no-repeat;
}
Unless someone has use cases that call for the property, I'd suggest keeping it simple and using an at-rule.
Other specs (most likely HTML) will probably find it convenient if we define a hook in the "Handling key presses" section to let them define what a particular element does when arrow keys are pressed and that element wants to respond (e.g. the <select>
element changing which is the currently selected <option>
).
EDIT: The discussion in this issue moved to a completely different, and more interesting topic. Repurposing the issue to talk about that.
What we discussed in our meetings and hallway conversations is that we'd start with a DOM-oriented API that allows us to 'find' an element - either the next/previous one in sequential order or spatially. I'd like us to clarify what this should find, and why. Here's why I think this really matters: We learn how to use things and when that changes it is disorienting and unfamiliar, this is why ARIA defines a number of interaction patterns like, for example, a set of tabs. If you're a keyboard user (I am frequently) then you learn how tabs work. When you see tabs, you know how you expect to interact with them. It's no different for other kinds of users. If I have a set of 4 tabs and focus is on the first tab, pressing tab again does not take me to the second tab, it enters the content of the active tab. Changing the selected tab is done with keyboard directional keys.
A lot of current approaches to accessibility for things like this currently involve a concept which is referred to as "roving tab index" or "roaming tab index" (or something, we can't seem to keep that one entirely straight :))... In this example, all the tabs are tabindex="-1" - they are not in the natural sequential focus, but they are programmatically focusable.
If I am on the first tab and I programmatically used our hypothetical API to find the next one, would we expect it to find the tabindex="-1" ones that are not hidden or only tabindex="0" ones ?
There is a difference between the focusable element and navigatable element.
An element which is navigatable but not focusable (e.g., scrollable area) can contain focusable elements.
If there is a JS API for knowing the element is navigatable or focusable, it will be useful for applying the focus navigation to the group of elements.
Here is the similar consideration in WICG discourse:
(https://discourse.wicg.io/t/extending-element-level-focus-apis/726)
Could we generalize element.selectSpatNavBestCandidate()
and element.sequentialNavSearch()
into element.nextFocusMove(direction)
?
direction
would be from a NavigationDirection
, that includes both sequential- and spatial navigation's steps; forward, backward, up, down , left, right.
We would then lack an API for asking "Which one, out of these focuables, would spatnav go to?", but we could instead ask "Which is the focusables spatnav/tabnav would go to?". I think the latter is more useful.
In the https://wicg.github.io/spatial-navigation/#find-candidates section, we try to limit the list of candidates to visible focusable elements. Should we limit the search to the optimal viewing region (taking the scroll-padding
property into account)?
Given that the next step uses hit testing to further narrow the list, that may not be necessary. But maybe.
The test cases in the repo cover a lot of scenario, but they're set up to be manual tests. We should refactor them and automated them as far as possible into something that could be submitted to web-platform-tests
The spec does not currently say anything about shadow DOM. It most certainly should say something.
For the current processing model to work correctly we need the thing that is focusable and the thing that is scrollable in an iframe to be the same thing. If the parenting relationship ends up wrong, the algorithm may do the wrong things. Between the host element, the document, the document element, the viewport, etc, it's non obvious.
We need to check it is defined correctly.
When focusable elements are nested, the inner one is neither to the top, bottom, left, or right of the outer one (typically, though it can with overflowing layouts).
This is particularly common when there are focusable things inside a scroller, since scrollers themselves are focusable.
The spec proposes to use the enter key to "navigate inside". This is introduced in the https://wicg.github.io/spatial-navigation/#overview section, and futher detailed in file:///Users/florian/src/spatial-navigation/index.html#processing-model.
Is this the best approach? I think so, but this is worth discussing. Here are a few arguments as to why this was specified the way it is:
Just relying on the tab
key isn't good enough, since not all spatial input devices have a tab
key (remote controls, gamepads, etc)
legacy implementations of spatial navigation have coped decently without this feature because much legacy content was crafted specifically for TV, and used very simple layouts where the situation does not arise. Making spatnav a feature of the general web means we need to make sure it is resilient to a broader range of situations, so we need something
The enter key is a "natural" answer to this question, and is always available on any spatnav capable input device (otherwise it couldn't click).
The enter key does not have pre-existing behavior of scrollable areas, so this addition does not conflict with anything
A possible alternative would be that:
This solution would also work, but it felt harder to understand for users: some scrollers can be focused, some cannot, and it may not be immediately clear why. Also, this makes it hard for a user to skip past a scroller, since its content would be directly focused. With the enter-key approach, whether to dive into an element or skip over it is the users's choice.
That said, it is a possible consideration. Note that the root is effectively special-cased to use this logic, as the downsides mentioned above do not apply to that case, and having to press enter to get to anything at all in the page would be silly.
The focusing steps are defined in HTML. They probably should reset the navigation starting point as well as the sequential focus navigation starting point
It seems a way to know the best candidate just with the direction argument would be convenient (e.g. elm.selectSpatNavBestCandidate("right");
),
but it may also be confusing:
Would this be expected to search only within the innermost spatnav container,
or to reproduce the full logic of running the spatial navigation steps,
and if so, does that include scrolling (probably not), or a special return value to indicate
that scrolling would have happened (maybe)?
Maybe we need a separate method for that.
This algo https://wicg.github.io/spatial-navigation/#navigation-steps includes a monkey-patch on HTML. Once things settled down, it should be submitted as a Pull Request.
The propagation of scrolling up the ancestor chain described in the spec (for the non spatnav case) is how Chrome and Safari behave, but Firefox is different. We should get the spec and all implementations to align.
Note: this may interact with #19
Conceptually I see spatnav as a key-hook that runs in the browser just before arrow-key scrolling. If spatnav didn't find anything to focus, it lets the standard arrow-key scrolling do its job.
navnotarget
could be used to signal: "spatnav's search failed, here's your chance to do something before we hand over control to the arrow-keys".
When handling navnotarget
, authors would still be able to find out, by looking at the focused element's container's overflow, if a scroll is about to happen or not.
The overscroll-behavior spec, which is currently in WICG but about to move to the csswg should be taken into account in the processing model (or the processing model should have hooks for it to influence its behavior).
In the initial proposal for nav-loop
which specifies an ability about the focus looping, it didn't regard the looping for each axis separately.
@frivoal pointed out considering define the focus looping for each axis.
How about adding values to nav-loop for it? Something like:
nav-loop: auto | repeat | repeat-x | repeat-y | no-repeat
Each value means:
or having separate properties can be another possibility for this:
nav-loop-x: auto | repeat | no-repeat
nav-loop-y: auto | repeat | no-repeat
The word select makes me think of text selections.
Best is a vague word. The best element is always the closest element, right?
Maybe calling the JS method something like closestFocusableArea()
would help authors understand the API better?
Definition: focus delegation is the behavior illustrated in this example.
As the example shows, focus delegation can be achieved using JS. We could also have level a property to turn that on automatically in a later level of the spec.
So if we want focus delegation as an option, the spec is fine as is.
However, if we would like focus delegation to be the default one that users get when authors have neither styled nor scripted the document, then it needs to be included in the processing model.
There is a recurrent complain coming form a11y people that sequential navigation (aka moving focus with the tab key) as it is today is not adequate for all use cases / all users, especially in the face of increasingly powerful tools in CSS to lay things out with relatively little connection with the document order.
This has led to suggestions that sequential navigation should follow the order
property in flexbox, or that it should follow "the visual order", or to the suggestion that there should be a nav-index
property, or that there should be a nav-next
property, or a nav-order
property, or a combination of the previous things with some mechanism to scope it to a DOM sub-tree.
There are multiple difficulties with that:
While the DOM tree has a natural order, a visually laid out document does not. It is in 2D, which has no inherent ordering, and on top of this color, contrast, size, movement, and more come to influence in what order things are typically perceived. This makes it far from trivial in the general case to find a linear order which could be described as "the visual order". This makes it neither easy to define an order the browser should go through, nor to find the right set of properties that authors could use to express it.
There seem to be multiple overlapping and conflicting requirements. Sighted mouse users who occasionally use the keyboard as a supplemental navigation aid expect different things form sighted people who primarily navigate by the keyboard, who in turn expect different things from blind or partially blind users who primarily navigate the document based on its logical structure.
Discussions in the CSS-WG and in the WICG meeting leading to the creation of this repo have generally agreed that at least some of the cases that had previously been expressed as a desire for reordered sequential navigation would probably be better server by spatial navigation. The general consensus is that a good way forward is to first focus on getting spatial navigation to work, then look for use cases that are still not adequately covered by the combination of (non-reordered) sequential navigation and spatial navigation.
To be most useful, such use cases should document:
I have created a wiki page to document these use cases. Contributions most welcome.
Also, since this has been a long running topic, linking to (and summarizing) previous conversations would probably be useful. There's a wiki page for that too.
I would like to encourage people interested in this issue in contributing to the two wiki pages above. With that information, and once we've made enough progress on spatial navigation (See the rest of this repository for this broader topic), it will be much easier to return to this topic and see what is left to solve.
Hello,
Let's assume that an author makes a web page that supports spatnav.
If a running browser supports spatnav by default, the author will disable
internal implementation (maybe author implemented using javascript) for
avoiding conflict. Maybe the author wants to prevent default spatnav.
But, How to know a browser supports spatnav or not in runtime?
For example, if we can use "if (window.spatnav_enabled)", we can control
above case.
Another use case for this information is polyfill. Polyfill library can
recognize spatnav feature by reading this information.
Currently, the spec says that find candidates works from
the set of all the focusable areas that are descendants of [the container]
The polyfill, on the other hand, does not include the focusable areas that are themselves descendants of other focusable areas within the container. In other words, the spec is recursive, while the polyfill is not.
The difference can be observed in this example, by focusing the topmost blue box, and trying to go up. With the polyfill, it will no go any higher, while the spec would cause red box to be focused.
It seems that the behavior in the spec is preferable, but it there may also be a performance concern.
I lean towards considering this a bug in the polyfill, but discussion is welcome.
We should do a thorough review of the processing model, to make sure that it consistently and correctly uses the correct tree (element tree vs box tree vs fragment tree...) and the correct ancestry relationships (parent vs containing block).
The spec currently claims that it is document elements that do. Is that correct?
When finding the candidate set, Chrome's spatnav includes focusables that would become visible after one (document*) scroll step in the navigated direction.
* We don't do this for scrollable divs but I consider that a bug. I believe spatnav should treat both document scrolling and element scrolling in the same way.
This behavior is not spec'ed. Should we?
Downsides:
Upsides:
The spec and the polyfill behave differently when the spatial navigation starting point, set by a click, is outside of the currently focused element. In the spec, it is only taken into account if it is inside, and ignored otherwise. I the polyfill, it is always used.
https://wicg.github.io/spatial-navigation/#key-presses
It currently has big gaps. These are not particularly related to spatial navigation itself, but that section should define the complete processing model for what happens when keys are pressed, and it currently only offers an outline.
(once this is complete, that section should probably move to another spec, as argued in #22, but either way it needs to be complete)
Let's add a container
-attribute to NavigationEvent
?
It will default to document.body unless spatnav-container
imposed a restriction on the search.
When dispatching the event, the browser has this information at hand already, so why not share this possibly important piece of information to authors?
The current API corresponds to the "7.3 Focus Navigation Heuristics" part of the processing model. An API for the "7.2 Navigation" seems useful as well, and would enable automated testing of a much larger part of the specification.
I'm thinking fewer events give authors a chance to write tighter JavaScript: one handler for all of the navigation-related reactions.
When authors tweak their apps' navigation, I imagine debugging being easier if they only need to break and assert things in one event handler (not 3). We will enjoy the same convenience when writing web platform tests.
Proposal: Give NavigationEvent
a reason
or action
attribute.
From an implementor's perspective I see two advantages:
By the way, the event's name does not need to include before. That's already implicit from being cancelable, right?
The element specified with spatial-navigation-contain
CSS property isn't focusable by default.
Also, focusable elements inside the non-focusable(tabindex=-1) spatnav container cannot be reachable by moving focus with spatnav.
But current polyfill works as moving the focus inside the non-focusable spatnav container.
e.g.)
<div tabindex=-1 style="left:110px; width:250px; height:100px; --spatial-navigation-contain: contain;">
<button class="box b3" style="top:40px; left:60px;"></button>
</div>
See: https://wicg.github.io/spatial-navigation/sample/api_spatnav_contain.html
Authors may want to trigger spatial navigation via script, with something like a elm.focusUp()
/ elm.focusDown()
/ elm.focusRight()
/ elm.focusLeft()
syntax.
In general, this would give more flexibility to authors to explore variants of the default spatnav behavior in an Extensible Web kind of way, possibly letting us standardize common uses of this feature further down the road.
For specific examples, this could be useful in cases like:
focusDown() instead
.However, overall, I would support this more for its enabling potential as a low level primitive than as a solution to high level problems.
While we're at it, we should probably consider introducing the equivalent for sequential navigation (aka tab-based focusing): elm.focusNext()
, elm.focusPrevious()
. A little tour around stack overflow shows that there's demand for these.
https://wicg.github.io/spatial-navigation/#key-presses
This should be an extension of UI-EVENTS's processing model
but UI-EVENTS does not have a fully defined processing model.
It does define the events, but does not define the algorithm which
is supposed to dispatch them and respond to cancellation and so on.
Therefore, this is being written as (a rough sketch of) what that
model should be.
Once this is sufficiently detailed and tested, this should be turned into
a pull request against UI-EVENTS.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.