Interacting with Tooltips and Previews

In this third part to a four-part series on adding Ajax to your web site, you’ll learn about tooltips and in-page previews. It is excerpted from chapter four of Adding Ajax, written by Shelley Powers (O’Reilly, 2007; ISBN: 0596529368). Copyright © 2007 O’Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O’Reilly Media.


As I stated in the beginning of this chapter, if you’ve been out to the Netflix or Blockbuster Online sites, you’ve seen how moving the mouse over a movie-related link or image pops up information about the movie, as well as a link to add the movie to the queue. This is probably one of the best uses of tooltips I’ve seen. It quickly provides more detailed information about an object rather than forcing users to go to another page. This is especially advantageous if the site user is browsing through many items. Tooltips can be used for anything: a shop, getting more detailed information about a row of data returned from a database, camera information associated with a photo, and so on–anytime you want to provide information about an object within a browsing context.

The only modifications required to convert the JIT application in the prevous section into a tooltip is to provide a bubble-like background for the text and position it so that it’s close to where the mouseover event occurs. Sounds simple, but it begins to add to the amount of code.

The following web page fragment is very similar to that shown in Example 4-7, with the addition of two more page elements: an h1 header and a standalone link. These are added to demonstrate that any page element can get a tooltip, not just form elements:


  <h1 id="header">Testing Tooltips</h1>

  <form action="ch04-08.xhtml"
  <legend>Personal info</legend>
  <a href="help.php?item=firstname" accesskey="f" title="opens help window for
  firstname field" rel="external">
  <label id="firstname" for="first">First Name:</label></a> <br />
  <input type="text" id="first" name="first" /><br />

  <a href="help.php?item=lastname" accesskey="1" title="opens help window for lastname
  field" rel="external">
  <label id="lastname" for="last">Last Name:</label></a><br />

  <input type="text" id="last" name="last" /><br />

  <input type="submit" value="Save" /> 
  <p><a href="" id="link">Tooltip for a hypertext link</a> </p>
<div id="help">

In the stylesheet, the tooltip help block is set to a bubble background:

     background-image: url(back.png);
     background-repeat: no-repeat;
     height: 200px;
     padding: 10px 0 0 10px;
     position: absolute;
     visibility: hidden;
     width: 150px;

The most significant change to the new application is in the JavaScript. For each element that has a tooltip, the mouseover event is captured in order to display the tip. The mouseout event is captured to then hide it. When the mouseover event fires, the mouse cursor position is captured so that the help element can be moved to approximately the same page location. Its position is also modified relevant to the bubble size, to put the pointed part of the "bubble" as close to the cursor position as possible. The only variation is with the top element. Since it’s at the top of the page, the vertical position of the tooltip is adjusted so that the tip’s top won’t go beyond the page top.

{mospagebreak title=Tooltips in a JavaScript File}

Example 4-8 shows the new JavaScript file.

Example 4-8. Providing tooltips for form and other elements

var xmlhttp;                // global XMLHttpRequest obj
var helpItem;               // current help item
var helpObj = new Object(); // cached help items
var posX; var posY;

// setup tooltip event
function addTooltip(ttObj) {

// attach tooltip events to objects
aaManageEvent(window,"load",function() {
   var items = document.getElementsByTagName(‘label’);
   for (var i = 0; i < items.length; i++) {
// get help from the server
function showHelp(evnt) {
evnt = (evnt) ? evnt : window.event;

  // get position
  posX = evnt.clientX;
  posY = evnt.clientY;

  // get XMLHttpRequest object if not set
  if (!xmlhttp) xmlhttp = aaGetXmlHttpRequest();
  if (!xmlhttp) return;

  helpItem = (evnt.currentTarget) ? :;
  var qry = "item=" + helpItem;

  // if cached item, print existing and return
if (helpObj[helpItem]) {

  // invoke help system
  var url = ‘help.php?’ + qry;‘GET’, url, true);
  mlhttp.onreadystatechange = getHelp;

// hide help bubble
function hideHelp() {
//I would suggest changing the class name instead of directly manipulating style properties//
// display help
function getHelp() {
if(xmlhttp.readyState == 4 && xmlhttp.status == 200) {
      helpObj[helpItem] = xmlhttp.responseText;

//position tooltip
function printHelp() {
  var help = document.getElementById(‘help’);
  help.innerHTML = helpObj[helpItem];
  y = posY – 130;
  if (y < 0) y = 10; = y + "px"; = (posX + 10) + "px";"visible";

View the tooltips in action in Figure4-2 .

Figure 4-2.  Tooltips in action, providing users with extra information

Again, it seems a lot of code to create a simple effect, but it’s also code that can be easily repackaged for use in a separate library. All you have to do is provide a way to create the tooltip object, passing in the location and content, and the tooltip object takes care of the rest. You could package more of the functionality, passing in just an element, and the tooltip object can take care of the event handling, though you’ll most likely need to provide the content unless you embed the contents as elements in the page rather than pulling it in from a web service.

An even simpler approach is to use an existing tooltip library. One such library is Tooltip.js, which is based on Prototype and script. You can download the most recent version of the library at

For effects such as those found on Netflix and Blockbuster, you’ll need to provide an image that has a transparent background. The only way to do this is to use a transparent GIF or a PNG image, though the latter doesn’t work that well with IE (not even with IE 7, which adds some odd color effects). You’ll have to layer your effect, providing a header and footer image, and a separate body image that can repeat along the vertical axis so that it can be resized based on the length of the contents.

One other important item to remember when providing this type of functionality is that tooltips don’t work if scripting is disabled. However, one workaround is to provide hypertext links around the element to open a separate page and use anchors for individual help items.

{mospagebreak title=In-Page Previews}

Sometimes the simplest technology can have the biggest impact. Of all the changes I’ve ever made at any of my sites, the one that people liked the best was also one of the simplest to implement: live preview.

Live preview echoes what a user writes as she types. It allows the user to see how the writing looks in the context in which it will be published, rather than within little windows in small forms. It does so before she submits the new or modified material. It’s also quite easy to implement a nonscript and/or accessible alternative: you provide a preview page where a user can review the writing before it’s permanently submitted. You can even provide both: live preview and a Preview button.

Where is live preview most useful? Anytime you ask for commentary from the web page reader. This includes email messages to be sent to customer service, comments on weblogs, feedback in product reviews–anyplace a user is writing more than a few words.

Live preview consists of listening for any activity in a form field, capturing the keys pressed, and echoing them to another page element. How you echo depends on how active you want the preview to be.

You can echo the letters as they’re typed for a true "live" experience. You can also provide a Preview button, but rather than opening up a new page, it will display the comment directly in the page. This approach doesn’t capture the keypress, and the data from the comment field is accessed only when the Preview button is activated.

Both approaches have good points and bad, which I’ll get into as we look at each individual approach.

{mospagebreak title=Live Echo Preview}

A live, echoed preview is relatively simple to set up. You begin by capturing the keyup event in either a form textarea or input element, and take all of the text and reflect it in an associated div element through the innerHTML property.

In the code, you do need to adjust the text, replacing the hard carriage returns with a break element, br. This keeps the text more or less in sync with the input.

Example 4-9 includes a complete application that uses live preview. Since the example is so small, the stylesheet and script are both included within the web page. The previewed text isn’t modified other than transforming the carriage break into a br. If you want to modify the text preview in other ways, such as stripping HTML tags to reflect what the comment will look like published, you’ll need to adjust the script accordingly. Remember, though, to keep the preview simple–the function to live preview the input is called whenever a keyup event happens in the form element.

Example 4-9. Simple live preview

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
<html xmlns="" lang="en" xml:lang="en">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Live Preview</title>
<style type="text/css">
background-color: #ccc;
   margin: 20px;
   padding: 10px;
width: 500px;
margin-right: 10px;
<script type="text/javascript" src="addingajax.js">
<script type="text/javascript">

manageEvent(window,"load",function() {

// echo the keypresses
function echoPreview(evnt) {
   var commentText = document.getElementById("comment").value;
   modText = commentText.split(/n/).join("<br />");
   var previewElem = document.getElementById("preview");
  previewElem.innerHTML = modText;
<form action="preview.htm" method="post">
<textarea id="comment" cols=50 rows=10></textarea> <br />
<input type="button" value="Preview" />
<input type="submit" value="Save" />
<div id="preview">

Whatever is typed into the textarea is reflected in the preview element.

The form also has Preview and Save buttons, both of which go to the preview.php page. From there, a user can save the comment or click the Back button to return to the form and continue editing. Neither requires script. Later on in this chapter, we’ll see how we can submit the form and display the new comment, all without leaving the page.

There’s one major problem with live preview, and that’s what happens when the page is served with an XHTML MIME type. The way that browsers implement innerHTML differs among applications, though most do support some version of innerHTML. However, what happens during live preview if you begin to add an XHTML tag differs dramatically between browsers. In Firefox 2.x and up, the incomplete tag throws dozens of errors until it’s completed, all of them saying the text is not well-formed XML. In Opera, however, the browser treats the incomplete tag as escaped content until the tag is closed, in which case, it then treats it as XHTML.

A workaround is to escape HTML characters, <, >, and &, replacing the modText generation with the following:

   modText = commentText.replace(/&/g, ‘&amp;’).replace(/>/g,
  > ‘&gt;’).replace(/</g, ‘&lt;’).split(/n/).join("<br/>");

This disables the links and other HTML. In nontrusted situations, you may want to consider completely stripping out all HTML.

The differences in behavior between browsers is significant enough that you’ll probably want to use live preview only with pages served as HTML, at least until the new HTML 5.0 standard comes out, which should clarify how a document fragment is managed. In the meantime, if you want XHTML and an in-page preview, I’d suggest you use the "Ajax" version of comment preview.

There are many implementations of live preview online, but I first saw it via a WordPress plug-in created by Chris Davis at http://www.

Please check back next week for the conclusion to this article.

Google+ Comments

Google+ Comments