views:

44

answers:

4

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, when 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

+1  A: 

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.

scunliffe
A: 

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

Steven Sudit
I'm not sure I understand what you mean. Could you elaborate a bit more?
Steve
@Steve: I'm suggesting that you solve this problem on the web server at the time you emit the HTML, not within the web client in JS. If your screen layouts are fairly constant, you can hard code the ID's of the left, right, up and down "neighbors" for any given control. Then the JS will only need to retrieve that ID and use it to look up the control. Admittedly, this is crude and perhaps hard to maintain, but I offer it as a workable solution.
Steven Sudit
Thanks. The only problem with this approach (at least in my case), is that there is no web server serving up the pages, which I did not mention. The pages are essentially static and installed on the TV, and can be updated on the client side only.
Steve
If they're truly static, they can be hard-coded. But if they're updated on the client side, then whatever does this updating would have to embed the LRUD names.
Steven Sudit
+1  A: 

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.

David Murdoch
+1 Interesting idea. I'll have to explore this. Thanks.
Steve
I updated my answer to provide some pseudo-code.
David Murdoch
lets us know if this works for you
David Murdoch
+1  A: 

I know this has already been answered but I've just stumbled upon this plugin - http://mike-hostetler.com/jquery-keyboard-navigation-plugin

Looks interesting, right?

Sam
+1 Thanks, very interesting.
Steve