Google TV

Using Google TV jQuery UI Library Key Navigation

Steven Hines, Google TV Developer Relations
January 2011

The Google TV jQuery UI Library provides a keyboard navigation system that can manage the selection for both keyboard and mouse navigation. It provides a large set of capabilities to handle complex pages, but it tries to provide intelligent defaults so that most pages will need only a little setup for it to work.

The navigation system:

  • Separates the page into multiple navigation zones, each with customizable behaviors.
  • Can navigate by page structure or by calculating the nearest item.
  • Can manage multiple layers of zones on a page, so pages with pop-up dialogs or overlaid content can easily move the selection back and forth.
  • Handles keyboard navigation to text input and select elements.
  • Maintains a single selected item for keyboard and mouse navigation, across zones and layers.

This article covers each of the primary objects involved in the navigation system, provides examples of how to use them, and goes into detail on some of the more advanced features.

Contents:

  1. Key Controller
    1. Navigation
    2. Zone Hierarchy
    3. Layers
  2. Key Behavior Zone
    1. Key Zone Creation Parameters
    2. Key Nav Selectors
    3. Key Mappings
    4. Key Actions

KeyController

The KeyController manages selection navigation for a site. It maintains a single "selected item" across multiple "zones" that are owned by independent compound controls.

The Key Controller listens for key events in the document as well as mouse enter and click events. This allows it to combine selection actions using the keyboard with those from the mouse (that is, if you select an item by hovering over it with the mouse, you can then move the selection from that point using the keyboard).

The main benefit to using the key controller for a page is that it unifies the selection over multiple controls that may have different use models and separate implementations.

For example, on a single page a SlidingControl might implement a behavior zone that allows it to slide in new sets of items as the user navigates to is left and right sides, and a SideNav control might select items by moving up and down, but for the page as a whole only one item can be selected at any one time. The Key Controller handles movement between these controls, maintains the single selected item, and allows multiple controls with different behaviors to exist on a single page at once without conflict.

By Document Structure

If useGeometry is false, the Key Controller will navigate to other items based only on the structure of the document, not its visual presentation. That is, it will look for the item to the right by looking at the next itemParent in a row, even if for some reason that itemParent has floated below the row and is not visually to the right.

This navigation method can be advantageous in cases where navigating back and forth between items should produce repeatable results (in some alignments navigating, say, left-right-left using Euclidean distance can result in returning to a different item). If a page has a fixed layout, navigating by document structure produces more predictable, controllable results.

Navigating Between Rows

The up and down arrows only select an item if the control has provided a zone with an itemRow Nav Selector. That is, for controls that have multiple rows. In addition, these directions will, by default, select the next item of the same index on a row where possible, and fall back to previous item indices until an existing item is found.

For instance, if item 3 on a row is selected, and the user moves to a row with only 2 items, the controller will select item 2 on that row. This behavior can be overridden if the zone specifies 'saveRowPosition=true', in which case selection moves to the item that was last selected on the row, or the first item if there was none.

selectHidden

By default, the Key Controller skips over items that are not visible (that is, the item or its parent has the style 'display: none' or the item's width and height are set to 0). If required this can be overridden by specifying 'selectHidden=true' for a zone.

The RotatorControl is an example of where this might be useful. In this case, the document structure consists of multiple rows of items, only one of which is visible at a time. When the user presses the up/down arrow, the selection should move to the previous/next row even though this row is hidden. The RotatorControl's scrollIntoView action hides the previous row and shows the new one.

By Page Geometry

If useGeometry is true the Key Controller will navigate to the next 'item' Nav Selector using a Euclidean distance calculation.

The controller roughly prefers the item closer in the direction of movement that is closest to the center of the current item in the axis perpendicular to the axis of movement. That is, if the user presses the left arrow, the item closest to the left edge of the selected item that is also closest to the vertical center of the selected item is preferred.

When calculating the closest item in the direction of movement, the controller will test a candidate item's closest edge and center. For example, when testing each item when moving left, it will use the closest of the left, right or center of the item when calculating the total distance between items. This handles cases where, for example, items of different sizes overlap each other.

Note that 'saveRowPosition' and 'selectHidden' have no effect when navigating by page geometry.

Overriding Default Navigation

The Key Controller allows each zone to provide key mappings for every key, including those for which it provides default behavior.

A zone provides a map for each key it wishes to override when it is initially created. After the key controller performs its default action for a key, if any, it calls any key mappings. It first calls any global key mappings and then any zone key mappings. Thus, global mappings can override default mappings, and zone mappings can override both global and default mappings.

All key mapping callbacks are provided with both the currently selected item and the newly selected item. Note: both the selected item and the newly selected item may be undefined; callbacks must handle these situations.

Key mapping callbacks may perform an action based on a key press (such as handling the ENTER key for a selection) or they may override any selection made either by default or a previous mapping.

For example, a zone may override "Left Arrow", and, if the value for the newly selected item is undefined, choose to explicitly choose a newly selected item. This may happen if, for example, the zone provides a paging behavior where the next item in a row not an element on the page, but only virtually present. When called in this situation, the control may determine from the currently selected item what 'virtual' item should be selected, and return it as a result from its mapping callback.

scrollIntoView and Transition Animations

The controller uses getFinishCallback to make sure that all animations that must be completed before any further events can be processed are actually completed.

For instance, imagine a control that is sliding pages, and it must slide 4 pages to get to the selected item. Additionally, this control cannot tolerate any other navigation events (mouse enter, click, arrow keys, etc) while the pages are moving. The control can ensure this by using getFinishCallback to track the completion of its animations. The Key Controller will not process any input events until the animations are over.

The Key Controller does this by supplying a getFinishCallback method to the scrollIntoView action callback. The callback then calls this method to retrieve a function to be called when the animation is finished. Each time getFinishCallback is called, it increments a counter. When the function it returns is called, the counter is decremented. When the counter returns to zero, the Key Controller resumes processing events.

The function getFinishCallback is generally called in an animation-finish callback. Code that acquires callback functions by calling getFinishCallback() must call that callback function once for each time it acquired. Key navigation events will be suspended until all acquired callbacks are called.

Below are examples of using the callback from getFinishCallback() to synchronize animations:

HTML5 transition

// Acquire the finish callback here.
var finishCallback = getFinishCallback();

function transitionDone(event) {
  someElem.removeEventListener('webkitTransitionEnd', transitionDone);

  // Make the callback when the transition has completed.
  finishCallback();
}

// Make sure transitionDone() is called at the end of the transition.
someElem.addEventListener('webkitTransitionEnd', transitionDone);

// Start the transition.
$(someElem).css({
  '-webkit-transition': 'all 2s ease-in-out',
  opacity: 0
});

jQuery.animate

// Acquire the finish callback.
var finishCallback = getFinishCallback();

// Tell jQuery.animate to call finishCallback when the animation is over.
someElem.animate({
  opacity: 0.0
}, finishCallback);

[Internally, the Key Controller maintains a reference that it decrements only after it has completed its work of changing the selection, so that further event handling is blocked until the entire process is complete, even if the animations complete immediately.]

Zone Hierarchy

The Key Controller tracks the hierarchical relationship of zones. This relationship is implicitly established when a zone is added to the controller with a container that is the parent or child of another zone's container.

The controller implements very specific behavior in these cases, allowing for controls that contain other controls to be notified of certain events that happen to their parent and child zones.

Entering Zones

For the purposes of managing selection, hierarchies are "flattened" across the page. That is, selection always "drops" down to the lowest child zones: when a parent zone gets the selection, it will transfer that selection to its first child, which will then transfer to its child, and so on, until a zone with no children receives the selection.

During this process, however, enterZone actions are still called. These are called in the following order: first, the enterZone of each parent zone of the initially selected zone are called in turn. Then the enterZone of the selected zone is called. Then each child entered has its enterZone action called.

Leaving Zones

When a zone is left, a similar process is followed. The initial zone's leaveZone action is called (with the selected item set) followed by the leaveZone action of each parent (with the selected item null) in turn.

Layers

The Key Controller supports multiple independent layers of control on a page. At any time, only one layer can be active. The active layer can be changed by setLayer(), setZone() to a zone in a different layer, or when the active layer or the last zone in the active layer is removed. Maintaining multiple layers allows the developer to create layers of controls on a page (for example, a login dialog over a content page) assigned to a layer without having to micromanage the key zones.

By default, all zones are placed into the 'default' layer. Pages that don't need layering never need to specify a layer name. Pages that have one primary layer can use 'default' for the main content, and then use new layers when placing dialogs over that content. Specifically, the page might create a dialog and add its items to a zone in a new layer, and then activate that layer. When the page is done with the dialog, the layer would be removed and the default layer re-activated.

In addition, each layer has its own page-wide key mapping that is active when the layer is active. This usage is generally preferred to setting a global key mapping.

A zone can also exist in multiple layers. This makes it easy to create headers, footers, toolbars, etc, that only need to be instantiated once and can remain active on as many other layers as desired. For instance, a page might have a "details" and "full screen", each in a separate layer, and have a header in both layers that remains active as the user switches between them.

KeyBehaviorZone

The KeyBehaviorZone describes the page navigation behavior inside a particular zone of the browser window. The zone is bounded by a container (generally a DIV) that describes the bounds of the zone. Objects of this type are used by the Key Controller.

The zone describes everything about the zone, including key mapping overrides, overrideable actions, selectors for default key navigation, styles for selection states, and so on.

var zoneCreationParams = { /* set param values */ };
var zone = new gtv.jq.KeyBehaviorZone(zoneCreationParams);

KeyZoneCreationParams

Creation of a new zone requires filling out a gtv.jq.KeyZoneCreationParams object with initialization parameters. The following table describes these:

Name Type Optional? Description
containerSelector string required jQuery selector that uniquely identifies the zone container on the page. The most reliable selector is for an ID (i.e., for jQuery starting with a '#'), but classes, names, etc, can also be used as long as they identify a single element on the page.
navSelectors KeyNavSelectors required A map of selectors that tell the Key Controller how it should determine what elements in the zone are items, item divs, and rows. These are used for the default navigation abilities of the key controller. See the Key Controller section for details.
selectionClasses KeySelectionCssClasses optional, recommended A map of classes used to style a selected item. Currently two styles: basic Default style for a selected item. Required if a zone desires a visual indication of the selected item. hasData Style for selected items that have a data item matching navigableData (see the next parameter).
keyMapping KeyMapping optional A map of key codes to callback functions. Any keycode can be overridden with a callback. See the documentation for Key Controller to see specifics on how to use these overrides.
actions KeyActions optional A map of action names to callback functions. See the documentation for Key Controller to find the available actions and their callback parameters.
navigableData string optional Indicates that items have a jQuery.data() value associated with them. If present, selectionClasses.hasData is styled for the element when it is selected. Use this to provide a visual indiciation that an item has an action associated with being chosen (i.e., with ENTER or with a mouse click).
saveRowPosition string optional If true, the Key Controller remembers the index of the item selected on the row, and re-selects it when selection returns to the row.
useGeometry string optional If true, the Key Controller will look for nearest-neighbors within a zone instead of using the static page/row/item layout of the page.
selectHidden string optional If true, the Key Controller will move the selection to an item even if it is hidden on the page. By default this is false.

KeyNavSelectors

This object tells the KeyController how the document is structured and thus how to find various elements for navigation.

Each of these attributes are jQuery selector strings that are used to identify the kind of element. In most cases, the elements of a particular type have a CSS class associated with them, and this class is used as the selector

For example, the following fragment describes two rows, the first with two items and the second with one item:

<div class="item-row">
  <div class="item-parent">
    <div class="item"></div>
  </div>
  <div class="item-parent">
    <div class="item"></div>
  </div>
</div>
<div class="item-row">
  <div class="item-parent">
    <div class="item"></div>
  </div>
</div>

In this case, the KeyNavSelectors object would be:

var keyNavSelectors = {
  item: '.item',
  itemParent: '.item-parent',
  itemRow: '.item-row'
};
Name Description
item Items are the navigable elements on the page. That is, they are the elements that can have selection and will be highlighed by the a selection CSS class (see below).
itemParent The immediate parent of an item on the page. Items in rows always have parents.
itemRow The parent of the itemParent. These represent horizontal collections of itemParents, each with an item.
itemPage A page of itemRows. This is only applicable if the document is implemented to have multiple pages that are not all visible at the same time but which are navigable.

KeySelectionCssClasses

This object tells the KeyController what CSS class to use to mark an item when it is selected. This might be an outline, border, partially opaque color, and so on, to visibly indicate to the user that the item has the selection.

Different classes can be provided to differentiate between items that are actionable by the user and those that aren't. For example, you might want a color highlight if an item has a URL associated with it, or gray if it doesn't.

Name Description
basic Default style for a selected item. Required if a zone desires a visual indication of the selected item.
hasData Style for selected items that have a data item matching navigableData (see the next parameter).

KeyMapping

A zone might wish to provide a key mapping to receive events about key presses on the page. For example, to receive a callback when the user presses the ENTER key (keycode 13) while an item was selected, the zone would supply:

function myEnterCallback(selectedItem, newSelected) {
  alert('Enter pressed');
  return new gtv.jq.Selection('none');
}

var keyMapping = {
  13: myEnterCallback
};

Each key mapping callback receives two parameters, both jQuery Elements, that say what the currently selected item is an, if the selected item is due to change, what the newly selected item will be.

The callbacks return a Selection object that tells the KeyController what to do next.

Status contains (string) Selected contains (jQuery.Element)
'none'
'skip'
'selected' The element to select instead.
Navigation Keys

The Key Controller provides default navigation key behavior for every zone that serves needs of the standard layout. This defaults can be overridden to choose a different selected item based on the input.

These default keys are:

Name Keycode Description
TAB 9 Move between zones on a page. Selects the next zone in the order they were added to the page. Wraps around to the first zone after the last zone.
Left Arrow 37 Moves to the previous item. (Moves to the item's parent's previous sibling's child.)
Right Arrow 39 Moves to the next item. (Moves to the item's parent's next sibling's child).
Up Arrow 38 Moves to the previous row. (Moves to the item's parent's parent's previous sibling's child's child).
Down Arrow 40 Moves to the next row. (Moves to the item's parent's parent's next sibling's child's child).
function rightArrowWrap(selectedItem, newSelected) {
  if (newSelected) {
    // If newSelected is defined, then the Key Controller was able to find
    // and item to the right, and we don't want to override it.
    return new gtv.jq.Selection('none');
  }

  // newSelected is undefined, so we must have reached the right end of the items.
  // We want to wrap around, so we look for the first item and return it.

  // Use jQuery to get the parent items in the row that contains this item.
  var parentItems = selectedItem.parent().siblings();

  // Use jQuery to get the first item in the row.
  var replaceItem = parentItems.first().children();

  // Return this item, which the KeyController will now make the new selection.
  return new gtv.jq.Selection('selected', replaceItem);
}

// Create a key mapping to call our wrap-around function when the right arrow is pressed.
var keyMapping = {
  39: rightArrowWrap
};

KeyActions

The Key Controller supports action callbacks for a variety of events that happen during operation.

Action Description Function Signature
scrollIntoView

Called by the controller every time a new item is selected.

This action is generally provided by any control that may move elements into and out of view. The control is responsible for making sure that, upon return, the newItem is being moved into view.

Because this is called on each selection change, it is advisable to detect if newItem is already visible and return immediately if so.

Parameter Name Type Description
selectedItem jQuery.Element The currently selected item, if any (may be undefined).
newItem jQuery.Element The newly selected item. This is the item that should be moved into view.
getFinishCallback Function The method to call to retrieve a callback function that must be called after any animation is completed (see below).
Return Type
None
click

Called when an item in the zone is clicked on by the mouse. Commonly overridden in combination with the key to handle user choice.

Name Type Description
item jQuery.Element The item in the zone that was clicked on.
Return Type
None
enterZone

Called when selection enters a zone or a child of the zone. Can be used to move the contents of the zone into view upon selection, etc.

The callback can return the item that should be selected upon moving into the zone. By default, the controller selects the item that was selected when the zone was left, if any. This is only used if the zone is selectable (that is, is the last child in a zone hierarchy).

Name Type Description
item jQuery.Element The item in the zone that was clicked on.
Return Type Description
jQuery.Element Item to be selected upon moving into the zone.
leaveZone

Called when selection leaves a zone or the parent of a zone. Can be used to move the contents of the zone out of view, etc.

Name Type Description
selectedItem jQuery.Element The item in the zone that was selected before leaving it.
Return Type
None
moveSelected

Called after selection has been moved to a new item. This is a notification; the zone can't do anything to change the outcome of the item selected.

Name Type Description
selectedItem jQuery.Element The currently selected item, about to be replaced by newSelected.
newSelected jQuery.Element The newly selected item.
Return Type
None

Authentication required

You need to be signed in with Google+ to do that.

Signing you in...

Google Developers needs your permission to do that.