开发者

Dom Traversal to Automate Keyboard Focus - Spatial Navigation

I'm going to start with a little background that will hopefully help my question make more sense.

I am developing an application for a television. The concept is simple and basically works by overlaying a browser over the video plane of the TV. Now being a TV, there is no mouse or additional pointing device. All interaction is done through a remote control. Therefore, the user needs to be able to visually tell which element they are currently focused upon. To indicate that an element is focused, I currently append a colored transparent image over the element to indicate focus.

Now, wh开发者_StackOverflowen a user hits the arrow keys, I need to respond by focusing on the correct elements according to the key pressed. So, if the down arrow is pressed I need to focus on the next focusable element in the DOM tree (which may be a child or sibling), and if they hit the up arrow, I need to respond to the previous element. This would essentially simulate spatial navigation within a browser.

I am currently setting an attribute (focusable=true) on any DOM elements that should be able to receive focus. What I would like to do is determine the previous or next focusable element (i.e. attribute focusable=true) and apply focus to the element.

I was hoping to traverse the DOM tree to determine the next and previously focusable elements, but I am not sure how to perform this in JQuery, or in general.

I was leaning towards trying to use the JQuery tree travesal methods like next(), prev(), etc. What approach would you take to solve this type of issue?

Thanks


The trick you may run into, is what is "down" vs. "right" vs. "left" etc. E.g. if you have content laid out as a table (using an HTML Table) (3x3 like a tic-tac-toe board) and you are in the center cell...

+-----+-----+-----+
|     |  1  |     |
+-----+-----+-----+
|  2  |  x  |  3  |
+-----+-----+-----+
|     |  4  |     |
+-----+-----+-----+

the squares 1,2,3,4 would be where you navigate to if you pressed up,left,right,down... but in the DOM order, they are defined in order 1,2,3,4

thus "down" would need to know to skip "3" and go to "4"... worse yet, there could be any number of "focusable" elements between 3 and 4, if you have a larger table. (my simple case is a 3x3 table, but you could have all kinds of nodes, subnodes, floating nodes, etc.)

Sample HTML:

<table>
  <tr>
    <td></td>
    <td>1</td>
    <td></td>
  </tr>
  <tr>
    <td>2</td>
    <td>x</td>
    <td>3</td>
  </tr>
  <tr>
    <td></td>
    <td>4</td>
    <td></td>
  </tr>
</table>

You might be best off, making each focusable element a button, whereby it will have (or you can set) a tabIndex... and then you can use the arrows to go forward/backward (only) through the controls.

Otherwise you'll need to build something smart enough to know "geometrically" what is down, left, up... from where you are.


At risk of offering a crude answer, have you considered pre-calculating the names of the controls to go to?


You can use $(element).offset().top and $(element).offset().left to get the absolute positions of all of the focusable elements on the page. You will then need to compare the element's positions/dimensions [x,w,width,height] to those of the currently focused element taking into account the intended direction for navigation.

For example: If UP is pressed you will need to find all elements ABOVE the currently focused element by comparing their offset().top+height() to the focused element's offset().top and then determine which of those elements is closest [to the currently focused element]

I began writing code for this but it seems to be pretty complicated. The idea is this:

var keys = {"UP":40,"DOWN":38/*etc*/};
var elements = $(".focusable");
var focused = $(".focused");
var positions = calculatePositions(elements); // returns [{"element":element,"dimensions":{"x": x, "y": y, "w": width, "h": height}];

$(document).keypress(e){
    var keycode = e.keycode,
    var candidates;
    // up
    switch (keycode){
        case keys.UP :
            candidates = filterToElementsAbove(focused, positions);
        break;
        case keys.DOWN :
            candidates = filterToElementsBelow(focused, positions);
        break;
        // etc...
        default;
            return true;
    }
    focused.removeClass("focused");
    focused = findClosestElement(focused, candidates).addClass("focused");
}

The code above won't actually work...but it could set you in the right direction.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜