Using DirectionalNavigation

payzer edited this page Jun 24, 2016 · 4 revisions

You don't have to do anything to enable automatic focus handling in your app. DirectionalNavigation is enabled by default, and will handle directional user input, determine the element that should receive focus, and set focus to that element.

You can disable directional navigation by setting the TVJS.DirectionalNavigation.enabled property to null, as described below.

To enable directional navigation, include the following script reference:

<script src="directionalnavigation-1.0.0.0.js"></script>

###FocusRoot The focus root determines the elements that are considered as targets for automatic focus navigation. Only descendants of the focus root are considered for focus navigation. Setting the focus root restricts the automatic focus algorithm to only look at descendants of the root element when deciding which elements can receive focus in response to navigation events. For example, you may have a UI where navigation is limited to only a portion of the page, and you could set the focus root to reflect that.

By default, the focus root is set to the <body> element. You can change the focus root by setting TVJS.DirectionalNavigation.focusRoot to the desired element.

For example:

var newFocusRoot = document.getElementById("subContainer");
TVJS.DirectionalNavigation.focusRoot = newFocusRoot;

To disable automatic focus handling entirely, set the TVJS.DirectionalNavigation.enabled property to false. To re-enable automatic focus, set the focus root to an element on the page.

Focus root can also be used to handle focus and navigation for a modal dialog. When launching a modal dialog, save the current focus root, and then set the focus root to an element on the dialog. When exiting the dialog, set the focus root back to the saved value.

###Default Focusable Elements By default, only the following elements are considered focusable.

  • <a>
  • <button>
  • <input>
  • <select>
  • <textarea>

To make other elements focusable, set a valid tab index on the element.

For example:
<div tabindex="0">This div is eligible for focus</div>

###Specifying Additional Focusable Elements To specify additional focusable elements, you can use the TVJS.DirectionalNavigation.focusableSelectors array. By adding classes to this array, you are telling the DirectionalNavigation algorithm to consider those elements focusable.

Example Let's say you have existing code from a web site that uses the class "btn" to style divs so they look like buttons. You would like these divs to be focusable, but don't want to modify your existing website code. To make these elements focusable, you can add the "btn" class to the focusableSelectors array like so:

TVJS.DirectionalNavigation.focusableSelectors.push(".btn") 

The focusableSelectors array takes any valid CSS selector as a string.

Note: If an element doesn't have a valid tab index and needs one to become focusable, the algorithm will automatically add one.

###Non-Focusable Elements The following elements are considered non-focusable, and will never be given automatic focus.

  • Elements that have the disabled property set
  • Elements with a tab index of -1 or undefined
  • Elements with a display style of "none"
  • Elements with a visibility style of "hidden"
  • Elements with a height or width of 0

These elements will not be given automatic focus even if they have a valid tab index.

Setting tab index to -1 only applies to the element itself, not its descendants. Descendants may still be considered for automatic focus, based on the preceding rules.

Likewise, for purposes of directional navigation consideration, disabling an element or control only applies to the element itself, not it descendants.

However, elements with a display style of "none" or visibility style of "hidden" will cause all of their children to be removed from the visual tree, so they will not be visible to or considered for directional navigation.

###Setting Initial Focus

In general, it's good practice to set initial focus to an element on the page. Otherwise, users will have to navigate to a UI element before being able to perform actions. Setting initial focus provides a natural, streamlined user experience.

The directional navigation library does not set initial page focus, so your app should do so. If your app does not set initial focus, focus will behave as though it is coming from the upper left corner of the screen.

###Overriding Next Focus You can override the directional navigation algorithm and give explicit instructions about where you want focus to go from specific elements. Doing so allows you to continue to use directional navigation for most of the page, but provide specific instructions for directional navigation from certain elements.

Manual focus intent is specified by setting the data-tv-focus-left, data-tv-focus-right, data-tv-focus-up, and data-tv-focus-down properties on an element. These properties specify the element that should receive focus based on directional navigation from that element.

For example, consider a layout such as:
A B C
D E F
G H I

The following HTML:
<div id="E" data-tv-focus-left="#D" data-tv-focus-right="#F" data-tv-focus-up="#B" data-tv-focus-up="#D" data-tv-focus-down="H"></div>

Specifies that, when the <div> with id 'E' has the focus:
• left will go to the element with id 'D'
• right will go to the element with id 'F'
• up will go to the element with id 'B'
• down will go to the element with id 'H'

Only the specified directions are overridden. For unspecified directions, directional navigation behaves normally.

One common use of focus overrides is to handle wrapping around the screen. For example, while on the right edge of the screen you may want navigating to the right to wrap-around to a control on the left edge of the screen. To do so, set the right next focus selector of the element on the right edge to specify the id of the element on the left edge. Typically you'd also set the left edge element to focus to the right edge element when a left navigation event is received.

###findNextFocusElement

The TVJS.DirectionalNavigation.findNextFocusElement method returns the element that the automatic focus algorithm would give focus to, based on navigation in the specified direction.

For example:

var nextElement = TVJS.DirectionalNavigation.findNextFocusElement(keyCode, null);    
nextElement.focus();

With findNextFocusElement, your app can perform manual focus handling while still taking advantage of the algorithm that automatic focus uses to determine the best focus candidate.

To use findNextFocusElement with manual focus handling

  1. Set the TVJS.DirectionalNavigation.enabled property to false to turn off automatic directional navigation.
  2. Handle keyDown events and respond to navigation keys.
  3. Call TVJS.DirectionalNavigation.findNextFocusElement to determine the element that should receive focus.
  4. Call element.focus to give focus to the next element.

Manual focus handling is one way to implement wrap-around behavior, although it is easier to use automatic focus and simply override next focus for the edge elements, as described previously in Overriding Next Focus.

Manual focus handling can be used when you need to perform animations between focus events, after the navigation event is received but before setting focus to the new element. However, if you just want to animate the element that receives focus, it is not necessary to do manual focus handling. Instead, respond to the onfocus event for the element, and then perform required animation on that element.

findNextFocusElement can also take an optional focusHintRectangle argument, which is a rectangle used to override the size and location of the last focused element. When focusHintRectangle is specified, the automatic focus algorithm will use the specified size and location instead of using the actual location of the element that last had focus. Use focusHintRectangle to implement wrap-around behavior, to navigate from a control that is handling its own focus internally, or to otherwise change where focus is considered to come from.

For example, when entering a new page from the right instead of the left, you would specify the right-hand side of the screen as the focus origination.

var nextElement = TVJS.DirectionalNavigation.findNextFocusElement(205 /* Gamepad DPad Left */, 
                                                                  {left: 1920, top: 0, width: 1, height: 1});  
nextElement.focus(); 

###Focus Handling Within a Control

Sometimes you'll want to use automatic focus handling for most of the elements on a page, but have a control that needs to handle focus for itself and its children.

When the control first gets focus, it should save the current focus root and then set the TVJS.DirectionalNavigation.enabled property to false. Then the control can handle navigation events and perform its own focus handling. When the control receives a navigation event that should cause focus to leave the control, the control can re-enable directional navigation by setting the TVJS.DirectionalNavigation.enabled property to true. Then, either set the new focus manually, or call findNextFocusElement to determine the element that should receive focus on exiting the control, and set focus to that element.

###Customizing Input You can customize which keys the directional navigation algorithm uses by adding to or removing from the TVJS.DirectionalNavigation.keyCodeMap object. The keyCodeMap object has 4 arrays, 1 for each direction: left, right, up, and down.

Example
Let's say you want to use w, a, s, d for navigation. You could add those keys to the keyCodeMap:

TVJS.DirectionalNavigation.keyCodeMap.left.push(65) // a  
TVJS.DirectionalNavigation.keyCodeMap.right.push(68) // d  
TVJS.DirectionalNavigation.keyCodeMap.up.push(87) // w  
TVJS.DirectionalNavigation.keyCodeMap.down.push(83) // s  

###Directional Navigation Layout Recommendations

If you use a grid-based layout and follow the recommendations in Designing for TV, automatic focus should work well with your UX. For other layouts or complex UI, there might be cases where automatic focus will not be able to determine the best focus candidate, or will choose results that you might not expect.

The following layout issues can cause problems, and should be avoided:

  • Overlapping UX, where one or more elements share portions of the same screen space.
  • Nested UX, where focusable elements are contained within other focusable elements.
  • Unaligned UX, where element edges don't line up.
  • Elements that are part of the layout but are not in view. For example, off-screen elements. Elements that are not in view, but that are focusable, will be considered as focus candidates.

Also, elements that are in scrollable regions but are not currently in view might be given focus and brought into view. Generally this behavior is by design, but you should be aware of this flow when designing your layout.

You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Press h to open a hovercard with more details.