Placed in: Home arrow Programming arrow Webdesign arrow Advanced keypress navigation with jQuery
Advanced keypress navigation with jQuery

A while ago, an article called "A fancy Apple.com-style search suggestion" was placed here. Many people loved it and already used the search suggestion in their latest web project.

Still, readers gave some criticism about the script. According to Jape, Simon and others the script was missing one vital element which the Apple search does have: keypress navigation (keyboard accessibility). For that reason, I'm presenting you a way you can improve that script yourself using advanced keypress navigation with jQuery.

Advanced Keypress Navigation

Check out the demo so you can try it yourself: There is a simple menu displayed which reacts to the Up and Down arrows, as well as Enter (or Return) to follow the link.

Demo Advanced keypress navigation   Download Advanced keypress navigation

The script is (a bit) advanced because of the extra functionality when the user combines the mouse hover and keypresses. Want to know how to create it yourself? Check out the source code or follow these steps in this tutorial.

HTML

The HTML that I came up with isn't that hard to understand: Simply a division with an ID, containing an unorderd list with links.

 
<div id="menu">
   <ul>
      <li><a href="http://www.marcofolio.net/">Marcofolio.net</a></li>
      <li><a href="http://feeds2.feedburner.com/marcofolio">Marcofolio RSS feed</a></li>
      <!-- more items could be added -->
   </ul>
</div>

The only thing we need to remember here is the menu-ID that we're going to use later.

CSS

I'm not going to add very many CSS details to this tutorial. All CSS stuff is pretty basic, no fancy-stuff going on. However, there is one CSS class that you should remember:

 
.itemhover { background-color:#b7b7b7 !important; color:#232323 !important; }

The itemhover class is added to highlight the selected item from the menu. The background-color and color are set to !important to make sure that color is displayed.

Keypress

Now it's time to get our hand dirty and get down to the coding part with jQuery. First things first; We'll need to store the current selected item from the menu, including the URL where that menu item links to. So, we'll declare two variables at the top of the script:

 
var currentSelection = 0;
var currentUrl = '';

After loading jQuery from Google, we'll need to add a keylistener to the whole page. I've added comments to the script to make it clear.

 
google.load("jquery", "1.3.1");
google.setOnLoadCallback(function()
{
   // Register keypress events on the whole document
   $(document).keypress(function(e) {
      switch(e.keyCode) { 
         // User pressed "up" arrow
         case 38:
            navigate('up');
         break;
         // User pressed "down" arrow
         case 40:
            navigate('down');
         break;
         // User pressed "enter"
         case 13:
            if(currentUrl != '') {
               window.location = currentUrl;
            }
         break;
      }
   });
});

As you can see, this part requests the keyCode whenever the user hits any key on the keyboard. When the user hits "Enter (or Return)", the script checks if the currentUrl has been loaded with an URL. If so, the location of the window follows that URL.

Both the Up and Down arrow call a (non-existing) function called navigate with one parameter: The direction. Let's see what we should do inside that function.

 
function navigate(direction) {
   // Check if any of the menu items is selected
   if($("#menu ul li .itemhover").size() == 0) {
      currentSelection = -1;
   }
   
   if(direction == 'up' && currentSelection != -1) {
      if(currentSelection != 0) {
         currentSelection--;
      }
   } else if (direction == 'down') {
      if(currentSelection != $("#menu ul li").size() -1) {
         currentSelection++;
      }
   }
   setSelected(currentSelection);
}

The function first checks if there already is a list item with the class itemhover. If there isn't any, the currentSelection is set to -1 (0 is the first list item).

On the first go, the user can only use the Down arrow (Up arrow doesn't have any use at this point). If the Down arrow is used, it first checks if the current selected item isn't already at the bottom of the list (by checking the size). If it isn't, the currentSelection is raised by 1.

Almost the same counts for the Up arrow. When pressed, it first checks if it isn't at the top of the list (index: 0).

The end of the function calls another function called setSelected. It expects one parameter: The index of the currently selected item. This function is very small, but does a lot.

 
function setSelected(menuitem) {
   $("#menu ul li a").removeClass("itemhover");
   $("#menu ul li a").eq(menuitem).addClass("itemhover");
   currentUrl = $("#menu ul li a").eq(menuitem).attr("href");
}

The first line removes all classes inside the list called itemhover. The second line adds the class to the selected menuitem (the parameter from this function). This is the class that we created in the CSS part above, remember? The last line sets the currentUrl parameter by retrieving the href from the selected menu item. By setting the currentUrl, the Enter (or Return) keypress now works too!

Now we already have a wonderful menu that reacts to the keypresses from the user (Up, Down and Enter). But what about the mouse movements?

Mouse movements

We could do this the easy way by adding a :hover to the CSS file. This way, when the user hovers an item, it gets highlighted. But what will happen if the user combines the two? When hovering the third item with the mouse and pressing on the Down key, it would highlight the first item.

To make this also work perfectly with the mouse, we'll enhance the script with some more stuff. First, we'll need to let the menu item "know" at what index there are positioned. This is really simple to achieve, but essential to make this work. When the user hovers the third item, there is no way the item itself would know it is the third. But by adding data, it does:

 
// Add data to let the hover know which index they have
for(var i = 0; i < $("#menu ul li a").size(); i++) {
   $("#menu ul li a").eq(i).data("number", i);
}

As you can see, this part loops through all the menu items and adds the index of that item (called number) to the item itself. We'll need that data when hovering with the mouse.

So, let's add a hover-listener to the menu:

 
// Simulate the "hover" effect with the mouse
$("#menu ul li a").hover(
   function () {
      currentSelection = $(this).data("number");
      setSelected(currentSelection);
   }, function() {
      $("#menu ul li a").removeClass("itemhover");
      currentUrl = '';
   }
);

The first parameter from hover is a function when hovering the menu item. Once again, the currentSelection is set by retrieving the number that we just placed inside the data. This way, the script "knows" which item needs to be highlighted and can use the arrow keys after selecting an item with the mouse!

The second function (on mouse leave) isn't that spectacular: It removes all itemhover classes from the menu and clears the currentUrl.

Conclusion and Download

That's about it! The whole script can be viewed when downloading the source files and you can test it on the demo page. These kind of techniques can really improve the user friendliness of your website.

Demo Advanced keypress navigation   Download Advanced keypress navigation

Of course, scripts can always be improved. What do you want to see differently or do you see any bug fixes? If you're going to use this somewhere on your next project, please share!


Tags:  keypress navigation jquery webdevelopment

Interested in this topic? You might enjoy another article I've written called

Did you like this article? Subscribe to my feed or email to keep updated on new articles.

Spread the word and submit to:
Digg!Reddit!Del.icio.us!Facebook!Technorati!StumbleUpon!Newsvine!Furl!Ma.gnolia!
Comments
Add NewSearchRSS
Bruce - Not working for me   2009-07-06 10:12:17
Gravatar image Hi Marco,

Just thought I'd let you know that your demo on partially works for me on an Intel MacBook Pro (mid-2009) with OS X 10.5.7 and Safari 4.0.1. Enter key works, navigation by mouse works, but the arrow keys on my built-in or attached keyboard do nothing. Arrow keys do work for me in Firefox 3.5 for Mac however.
Marco - Fix   2009-07-06 10:36:14
Gravatar image Hi Bruce! First, thanks for your comment.

I checked and you're totally right. I did find it strange, because I want 100% positive it worked on Safari before.

After some Googling, I found out why it stopped working. Check out the following code:
Quote:
$(document).keypress(function(e) {

For some reason, when "keypress" is used, Safari can't retrieve the keyCode for the arrow keys. This is how the function looked before and does work on Safari:
Quote:
$(document).keydown(function(e) {

When using "keydown", it does work fine on Safari.

The downside of "keydown" is, that when you hold the arrow key, it doesn't move to the next object. That's why I changed it to "keypress".

Anyway, thanks for noticing and I hope you can work with the fix!
Nathan Logan - A potential optimization   2009-07-06 21:19:59
Gravatar image Good stuff. I like how you handled several things.

By way of optimization, you could supplant your use of .data for .index. That is, in place of this code:
Quote:
currentSelection = $(this).data("number" );

You could use:
Quote:
var currentSelection = $("#menu ul li a" ).index(this);

This would also allow you to completely remove this code:
Quote:
for(var i = 0; i < $("#menu ul li a" ).size(); i++) {
$("#menu ul li a" ).eq(i).data("number", i);
}

The gains are that you are using native jQuery functions and that you keep the data object namespace clear of potential conflicts (particularly since "number" could be a popular one).

Just an idea - but nicely done!
Marco - Good thinking, thanks!   2009-07-06 21:26:48
Gravatar image Hi Nathan! First off, thanks for your comment, feedback and compliment.

You're totally right, that would absolutely be much prettier. I wasn't aware of the ".index" method (still learning jQuery every day, getting to know it better and better ;) ), so thanks for letting me know. All I knew is that I needed the "index" and I placed it in the "data", instead of using the (native) "index" function.

Anyhow, learned something new (again) today; Thanks a lot!
Raymond Selda   2009-07-07 14:39:20
Gravatar image Very nice tutorial. I will definitely keep this in mind. Thanks for sharing.
Qamer - Its not wokring in IE   2009-07-17 08:35:12
Gravatar image Hi

I am not able to run this key press navigation in IE, i am using IE 7. Please help me to figure out this issue.


Qamer
TKF   2009-08-08 00:59:50
Gravatar image For me example also didn't work in IE6. I modified it, if you use keyup instead of keydown it will work fine in IE. But as Marco said if you hold the key nothing will happen.
Phaoloo   2009-07-19 14:51:12
Gravatar image Hmm, it might not work on IE, but others is OK. Thanks for the tip.
TKF - Great Work   2009-08-08 00:50:24
Gravatar image Thanks for this example,using it i modified fancy apple.com style suggestion so it is now supports keyboard.
I had some problems with addclass. Don't know why when class was dynamically added to suggestions div there was no style change. Instead of adding style i used changing css property.
Mariano - code   2009-11-06 08:13:28
Gravatar image I've failed to modify the previous code for apple style suggestion with this one. Could you provide the code you wrote? Thanks!
Jannis Gerlinger - Very Nice   2009-11-02 14:22:24
Gravatar image I like it! Very nice - keep going !
JB   2009-11-04 22:23:26
Gravatar image How do I enable the first item of my list to have the .itemhover? I tried this line of code prior to the start of the functions, but it didn't work (using FF3.5):

Code:
$("#menu ul li a:first";).addClass("itemhover";);
Itsashirt T shirts - Thanx   2010-01-13 00:03:20
Gravatar image Thank you for this keypress script, I will try it immediately...
jackocnr   2010-05-12 18:33:14
Gravatar image Thanks so much for these tutorials - exactly what I was looking for!

I found that the mouse hover thing didn't work on newly created suggestions (i.e. those fetched from Ajax requests). This was solved by using the jQuery live() function, once with mouseenter and once with mouseleave.

Also I added functionality for dealing with mouse clicks i.e. clicking off the form closes the suggestions:

Code:

$('body').click(function()
{
$('#suggestions').hide();
}
$('#search-input').click(function(event)
{
event.stopPropagation();
}


Also it's nice to allow your users to press escape to close the suggestions (like google do), but that required a re-structure of the example, but I'm happy to post it if people are interested.
Ben   2010-05-13 13:29:01
Gravatar image Is there a way I can use this script without internet? I mean, now it uses the external Jquery library at google... I need to have the library stored on my computer somehow. Is it possible? :)
Aks   2010-05-21 06:04:34
Gravatar image Thanks for this example. But keydown/key up doesn't work when scrollbar is present for tag. Any solutions?
Read more...
Name:
Email:
  Gravatar enabled.
Website:
Title:
UBBCode:
[b] [i] [u] [url] [quote] [code] [img] 
 
 
:angry::0:confused::cheer:B):evil::silly::dry::lol::kiss::D:pinch:
:(:shock::X:side::):P:unsure::woohoo::huh::whistle:;):s
:!::?::idea::arrow:
 
Unsubscribe from e-mail notifications.
 
< Prev   Next >
Subscribe

Subscribe to Marcofolio