Sample file

Below is a section excerpt from <select> Something New, Part 2 containing links, blockquotes and blockquotes containing definition lists. View the print preview to see what our script has done.

Aside: the <select> and onchange

I prefer to do much of my initial development testing in Firefox, primarily because I find the Web Developer Toolbar, ColorZilla, JavaScript Console and DOM Inspector to be indispensable tools. For the most part, Firefox (and Mozilla browsers in general) has great support for web standards and accessibility, but I did come across one oddity that threw me for a loop. It seems Mozilla has the only family of modern browsers (in my experience, at least) which does not register the onchange event when the keyboard is used to change the value of a <select>. These browsers only register an onchange event when focus is taken away from the <select>. This follows the guidelines in the HTML 4.01 spec:

The onchange event occurs when a control loses the input focus and its value has been modified since gaining focus. This attribute applies to the following elements: INPUT, SELECT, and TEXTAREA.

but it does not follow the accessibility recommendations set forth in UAAG 1.0 Test Suite for HTML 4.01 in Checkpoint 1.2, Provision 1:

Checkpoint 1.2 Activate event handlers (Priority 1)
Provision 1: Allow the user to activate, through keyboard input alone, all input device event handlers that are explicitly associated with the element designated by the content focus. Using the keyboard or an assistive technology that emulates the keyboard, select a value from the menu to trigger the onChange event.

Checkpoint 9.5, Provision 1 also touches on a related, though contradictory, premise:

Checkpoint 9.5 No events on focus change (Priority 2)
Provision 1: Allow configuration so that moving the content focus to or from an enabled element does not automatically activate any explicitly associated event handlers of any event type.

What’s a web developer to do? Well, it is my opinion that in the case of a conflict between the spec and accessibility, assuming a good case can be made for the accessibility side, accessibility should win.

That said, we need a way to make Mozilla browsers behave like the other browsers do, so we can tie into the onchange event of the original <select>. At first, I created a function to run when the onkeydown event took place on the <select>. It checked for certain keys (up, down, left, right, home, end, etc.) and then triggered the faux-<select> to change the displayed list item accordingly:

function selectReplacement(obj) {
  ...
  // when a key is pressed, get its keyCode and perform
  // the required actions to update the faux-<select>
  obj.onkeydown = function(event) {
    // grab the selectedIndex
    var idx = this.selectedIndex;
    // decide what do do based on the keyCode
    switch (event.keyCode) {
      // move to previous item
      case 37: // left
      case 38: // up
        if (this.prev >= 0) {
          selectMe(ul.childNodes[idx-1]);
        }
        break;
      // move to next item
      case 39: // right
      case 40: // down
        if (this.next < ul.childNodes.length) {
          selectMe(ul.childNodes[idx+1]);
        }
        break;
      // go to the beginning of the list
      case 33: // page up
      case 36: // home
        selectMe(ul.firstChild);
        break;
      // go to the end of the list
      case 34: // page down
      case 35: // end
        selectMe(ul.lastChild);
        break;
    } 
  };
  …
}

This worked fine in every browser but Opera, which for some reason kept jumping to every other item, up and down the list. Obviously, this won’t do, so I began looking for another way to make Mozilla behave the way we need it to (and, in my opinion, feel it should) and, after banging my head against the wall for nearly a day, I came up with this simple way of equalizing the event models:

function selectReplacement(obj) {
  …
  // equalize the event models of onkeypress and onchange
  // for the replaced <select>
  obj.onkeypress = obj.onchange;
  …
}

It’s my experience that the simplest solutions often aren’t the most obvious and forthcoming ones, at least not in my brain.