andy-williams.net

Web application keyboard navigation

I’m trying to improve my javascript/JQuery skills.

As a result, what follows might be child’s play to most, but it is mainly for my development, if there is anywhere you can see where any of this can be improved please let me know.

A recent post by Scott Hanselman got me thinking about about the way the users interact with the systems I build. I know that personally, when using Visual Studio I try to touch the mouse as little as possible, knowing that any task I can undertake with the keyboard is going to be faster and make me more productive, than if I have to use the mouse.

I should allow users of my systems to undertake tasks using just the keyboard.

The first task I set to allow keyboard access to was simple navigation through my web app, and in particular, drilling down into a table. In his post, Scott Hanselman talks about the emerging standard of using the 'j' and 'k' keys to move up and down items, examples of these being Gmail, Google Reader and Twitter, so I set out to see if I could put together some javascript to move up and down an html table and select a row to navigate to.

Imagine we have the following table contained within an ASP.NET MVC view.


<table>
    <thead>
        <tr>
            <th>Id</th>
            <th>Description</th>
            <th>Link</th>
        </tr>
    </thead>

    <tbody>
        <tr>
            <td>1</td>
            <td>Description of a widget</td>
            <td>@Html.ActionLink("View", "View", new { id = 1 })</td>
        </tr>

        <tr>
            <td>2</td>
            <td>Description of a widget</td>
            <td>@Html.ActionLink("View", "View", new { id = 2 })</td>
        </tr>

        <tr>
            <td>3</td>
            <td>Description of a widget</td>
            <td>@Html.ActionLink("View", "View", new { id = 3 })</td>
        </tr>
    </tbody>
</table>

We know that when a row is selected by the user, we want them to be navigated to the same MVC action as is specified in the ActionLink in each row. Lets add an extra piece of data to each row which simply contains the url we wish to navigate to if the given row is selected. We will also set the class attribute of the td element to selectUrl so we can easily locate it later.

<tr>
  <td>4</td>
  <td>Description of a widget</td>
  <td>@Html.ActionLink("View", "View", new { id = 4 })</td>
  <td class="selectUrl">@Url.Action("View", new { id = 4 })</td>
</tr>

This leaves us with the following

Now we know those urls are there we can just hide them

<td class="selectUrl" style="visibility: hidden">@Url.Action("View", new { id = 4 })</td>

Right, now we have our table setup we need to write the script which allows us to navigate the table.
To do this, lets use the jquery.hotkeys library which was referenced in Scott Hanselman’s post. We first of all need to identify the table as one which allows this kind of selection, to do this, let’s set the class attribute on the tbody tag to selectable.

<tbody class="selectable">

Now to write the script. We know that we will want to listen for three keys; j, k, and return, so lets declare this in our script

$(document).bind('keyup', 'k', moveDown);
$(document).bind('keyup', 'j', moveUp);
$(document).bind('keyup', 'return', select);

Here we’re just declaring that when the j, k, or return keys are pressed, the moveDown, moveUp, and select functions should be called respectively (don’t worry, we haven’t written these yet :-) ).

Next lets declare the variables we will be using, we need one to keep track of the currently selected index, and two to store the minimum and maximum possible indexes. We also set the max index to be the number of tr tags in our table’s tbody tag.

var selectIndex = 0;
var minIndex = 0;
var maxIndex = 1;

$(function () {
    maxIndex = $('.selectable tr').length;
});

Right, in order to show that a row is selected, we’re going to set its background colour to Highlight, when we do this, we’ll first need to reset each row to the default background colour, let’s write a function to achieve this.

function unselectAll() {
    $(".selectable tr").css("background-color", "#efeeef");
}

Now we can start to do some work. Here’s the function to move the selected index down

function moveDown() {
    unselectAll();

    selectIndex++;
    if (selectIndex > maxIndex) {
        selectIndex = maxIndex;
    }

    var selector = ".selectable tr:nth-child(" + selectIndex + ")";

    $(selector).css("background-color", "Highlight");
}

All we are doing here is clearing the highlights by calling our unselectAll() function, incrementing the selectIndex variable, selecting the row we need to highlight, and then applying the highlight. Simple.

To move the index back up, we just do the opposite

function moveUp() {
    unselectAll();

    selectIndex--;
    if (selectIndex < minIndex) {
        selectIndex = minIndex;
    }

    var selector = ".selectable tr:nth-child(" + selectIndex + ")";

    $(selector).css("background-color", "Highlight");
}

All that's left to do now is the actual selection of a row. To achieve this, all we need to do is grab the data marked with the selectUrl class attribute we declared earlier and navigate to the url contained within.

function select() {

    if(selectIndex > 0) {

        var selector = ".selectable tr:nth-child(" + selectIndex + ")";

        var url = $(selector).find(".selectUrl").html();

        window.location.href = url;
    }
}

And that's it we're done, simple! :)

kick it on DotNetKicks.com


Categorised as: Javascript


Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>