You are here

Make HTML Form Navigation Behave Like A Spreadsheet

Corey Pennycuff's picture

If necessity is the mother of invention, then clients are the source for interesting programming problems, and the need to solve those problems are a never-ending source of discovery for programmers. Such was the case for a recent project in which a lot of data had to be entered quickly and efficiently into an HTML form. The customer was accustomed to using spreadsheets, and wanted, most specifically, for the form to replicate the action of a spreadsheet when pressing enter. To put it plainly, they wanted the focus to go to the next row when they pressed enter.

The normal behavior for a form is that the browser will submit the form when enter is pressed (unless the element is a multi-line text area). Preventing this is easy enough using jQuery's keypress() event and listening and acting on the keyboard input as appropriate.

Raw Code

target = "input.some-identifier";
$(target).keypress(function(event) {
if ( event.which == 13 ) { // Enter key
  event.preventDefault();
  set = $(target);
  for (i = 0; i < set.length; i++) {
        if (set[i] == this) {
          if (i + 1 == set.length) {
                // On the last item, focus on first
                $(set[0]).focus();
          }
          else {
                // Focus on next item
                $(set[i + 1]).focus();
          }
        }
  }
}
});

All that is needed in this code is a jQuery selector (the target variable) which does not have to be an input as shown here, but any valid selector that makes sense in your implementation. In this code, whenever the browser focus is on one of the selected fields and the user presses enter, the focus will be moved to the next element in the document that matches the selector. The code will also wrap-around if the user is on the last instance on the page.

You may notice that this code re-computes the set variable that contains the elements to loop through. This was done because the form was dynamic and there could be one or more additional fields that get appended after the initial script runs. Therefore, when deciding which element should receive focus next, the entire set is re-assembled so that none are left out.

Code as a Drupal Behavior

I was using this in a Drupal site, so the JavaScript needed to be added using Drupal.behaviors, so that the code would be re-run as appropriate when the page contents are updated via AJAX. The modifications are relatively straightforward and are shown below as they were implemented for Drupal 7. Notice that we now include the context variable, which is used when applying this code to only a subset of the page (the part that is loaded on an AJAX request, for example).

(function ($) {
  Drupal.behaviors.YOUR_MODULE_NAME = {
    attach : function(context) {
      target = "input.some-identifier";
      $(target, context).keypress(function(event) {
        if ( event.which == 13 ) { // Enter key
          event.preventDefault();
          set = $(target);
          for (i = 0; i < set.length; i++) {
            if (set[i] == this) {
              if (i + 1 == set.length) {
                // On the last item, focus on first
                $(set[0]).focus();
              }
              else {
                // Focus on next item
                $(set[i + 1]).focus();
              }
            }
          }
        }
      });
    }
  };
})(jQuery);

Conclusion

This method is not only easy, but it would be fairly simple to extend the functionality to both rows and columns alike and to respond to more than just the enter key (like the tab and arrow keys). Also, I would like to point out that I am not a JavaScript programmer, so there may be more efficient ways of coding a solution, but this method did work for the limited time that I had. Let me know in the comments if you have any improvements!

Tags: