O'Reilly logo

JavaScript Cookbook by Shelley Powers

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

14.4. Adding Keyboard Accessibility to a Page Overlay

Problem

You’ve created a page overlay in order to display a larger image (or text, or other content), and you want it to be keyboard accessible.

Solution

Add keyboard listening to the page to complement the mouse events:

// mouse click on image within link
function imgClick() {
   var img = this.firstChild;
   expandPhoto(img.getAttribute("data-larger"));
   return false;
}

// key press on image within link
function imgKeyPress(evnt) {
   evnt = (evnt) ? evnt : ((window.event) ? window.event : "");
   var keycode = (evnt.which) ? evnt.which : evnt.keyCode;
   if (document.getElementById("overlay")) {
     if (keycode == 27) {
       restore();
       return false;
     }
   } else {
     if (keycode == 13) {
        var img = this.firstChild;
        var src = img.getAttribute("data-larger");
        expandPhoto(src);
        return false;
     }
   }
}

Discussion

The first step to adding keyboard accessibility into a web page is to either use elements that can receive keyboard focus (a, area, button, input, object, select, and textarea), or use the tabindex="0" setting on the element, which will make the element focusable.

The second step is to capture keyboard activity in addition to the mouse events. In Example 14-2, I’ve taken Example 13-3 from Recipe 13.6, and modified what was a mouse-event-only application to also accept keyboard events. The example creates a page overlay and displays a larger image when a person either clicks on a thumbnail image in the original page, or presses the Enter (Return) key when the image has the focus. Clicking the expanded image or pressing the Esc key removes the overlay, and returns the page to its original state.

Example 14-2. Making an overlay/photo display page keyboard accessible

<!DOCTYPE html>
<head>
<title>Overlay</title>
<style>
img
{
  padding: 5px;
  border-style: none;
}

.overlay
{
   background-color: #000;
   opacity: .7;
   filter: alpha(opacity=70);
   position: absolute; top: 0; left: 0;
   width: 100%; height: 100%;
   z-index: 10;
}
.overlayimg
{
   position: absolute;
   z-index: 11;
   left: 50px;
   top: 50px;
}
</style>
<script>

// expand photo when a/img is clicked
function imgClick() {
   var img = this.firstChild;
   expandPhoto(img.getAttribute("data-larger"));
   return false;
}

// if overlay is open, and ESC, close overlay
// account for keydown event in page
function imgKeyDown(evnt) {
   evnt = (evnt) ? evnt : ((window.event) ? window.event : "");
   var keycode = (evnt.which) ? evnt.which : evnt.keyCode;
   if (document.getElementById("overlay")) {
      if (keycode === 27) {
       restore();
       return false;
     }
   } else {
     if (keycode == 13) {
        var img = this.firstChild;
        var src = img.getAttribute("data-larger");
        expandPhoto(src);
        return false;
     }
   }
   return true;
}
// create overlay, expand photo
function expandPhoto(src) {

   // create overlay element
   var overlay = document.createElement("div");
   overlay.setAttribute("id","overlay");
   overlay.setAttribute("class", "overlay");

   // IE7
   // overlay.id="overlay";
   // overlay.className = "overlay";

   document.body.appendChild(overlay);

   // add image
   var img = document.createElement("img");
   img.src = src;
   img.setAttribute("id","img");

   // set tabindex, for focus
   img.setAttribute("tabindex","-1");

   // style image
   img.setAttribute("class","overlayimg");

   // IE7
   // img.className = "overlayimg";

   img.onclick=restore;
   img.onkeydown=imgKeyDown;

   document.body.appendChild(img);

   // focus on image in overlay
   img.focus();
}

// remove overlay and image
function restore() {

 document.body.removeChild(document.getElementById("overlay"));
 document.body.removeChild(document.getElementById("img"));
}

// add click and keyboard events
window.onload=function() {
   var aimgs = document.getElementsByTagName("a");
   aimgs[0].focus();
   for (var i = 0; i < aimgs.length; i++) {
     aimgs[i].onclick=imgClick;
   }
}

</script>

</head>
<body>
<p>Mouse click on image, or use keyboard to move to photo and hit
ENTER to expand the photo. To close expanded photo, hit ESC or
mouse click on image.</p>
<a href="dragonfly2.jpg"><img src="dragonfly2.thumbnail.jpg" data-larger="dragonfly2.jpg"
alt="image of common dragonfly on bright green and pink flowers" /></a>
<a href="dragonfly4.jpg"><img src="dragonfly4.thumbnail.jpg" data-larger="dragonfly4.jpg" 
alt="Dark orange dragonfly on water lily" /></a>
<a href="dragonfly6.jpg"><img src="dragonfly6.thumbnail.jpg" data-larger="dragonfly6.jpg"
alt="Dark orange dragonfly on purple water lily" /></a>
<a href="dragonfly8.jpg"><img src="dragonfly8.thumbnail.jpg" data-larger="dragonfly8.jpg"
alt="Dragonfly on bright pink water lily" /></a>
</body>

When I opened the new image, I assigned it tabindex of –1, to set keyboard focus. To ensure the application works with scripting disabled, the link references the larger image. When scripting is enabled, the image shows in the overlay; with scripting disabled, it opens in a separate page.

A click (rather than keypress) event is triggered when the Enter key is pressed and the focus is on one of the standard focusable elements: a, area, button, input, object, select, and textarea.

The tabbing did not work with Firefox on the Mac. It does work with Firefox on Windows, and hopefully, eventually, will work on the Mac, too. I also tested the application with Opera, Safari, and Chrome, and it worked fine. You do have to use Shift + the arrow keys to move among the images with Opera.

Safari overhauled its event system with Safari 3.1, and you no longer get a keypress event when clicking a noncharacter key. When the overlay is open, rather than capture the keypress event, you need to capture the keydown event if you’re using the Esc key to return the page to normal.

The application worked out of the box for IE8. To make the page work with IE7, the use of setAttribute and getAttribute with the class attribute should be changed to direct assignment (the downloadable example code contains a workaround).

As you can see, using tabbing within a web page is somewhat challenging. However, the future looks bright for this capability, with the new tabindex instructions in HTML5 that clarify tabbing and tabindex behavior. Instead of having to wrap images in links to make them accessible, we can just assign them a tabindex="0". However, for nonfocusable elements such as img, you do need to capture keypress and click events.

Providing keyboard access is absolutely essential for folks using AT devices, as well as people who have impaired movement. It’s also an important capability for those who are using devices that may have limited mouse capability, such as some phones. And it’s a nice enhancement for people who just prefer to use the keyboard whenever possible.

See Also

See Recipe 13.6 for a more in-depth explanation of the mouse events for the application. John Resig has an interesting post on the Safari 3.1 event-handling decision at http://ejohn.org/blog/keypress-in-safari-31/. Recipe 12.15 has more information on using setAttribute with CSS style properties.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required