User Action






User Action

Action, Change, Click, Control, DHTML, DOM, Events, Keyboard, Mouse, Move, Type, Widget

User Action


Goal Story

Pam is booking a trip on the corporate travel planner. She sees a form with the usual fields and clicks on location. Suddenly, a list of cities fades in beside the form, and Pam selects Paris. Beside the city list, a second list appears, this one showing approved hotels. Pam chooses the Hilton, and both lists disappear. Pam's pleased to proceed with the destination on the updated form, which now reads, "Paris Hilton."

Problem

How can the application respond to user activity?

Forces

  • A rich application allows users to interact with it, frequently and in different ways.

  • Responses must be as quick as possible, so as to streamline performance, keep user attention, and help the user understand the application's cause-effect model.

  • Using form submissions as the only response to user activity is slow and limits interactivity.

Solution

Handle most User Actions within JavaScript, using event handlers. The essence of Ajax is rich browser-based interaction, and DOM events are the technology that make it happen. DOM objects can register event handlers, functions that are notified when events occur. This callback model should be familiar to anyone who's worked with desktop GUI frameworks.

Event Handling, Behind the Scenes

If you've not worked with a modern user interface framework such as Java's Swing, you might wonder how JavaScript events work. The answer is that, behind the scenes, the operating system tells the browser what the user's doing, and the browser then passes this information to the script. For instance, the operating systemmonitoring keyboard and mouse inputmight notice that the user hit "X" while the keyboard focus was on the browser window. It will then notify the browser, and the browser will decide which element within the current web page has keyboard focus. If that element happens to have an associated keypress event handler, the browser will arrange for it to be invoked. Just prior to invoking it, it will construct an event object indicating the key that was pressed ("X"), the object that had focus at the time, and auxiliary information like the current mouse location and the current time. The browser will derive some of this information from the previous operating system notification, so other information might be queried there and then. When the event handler is invoked, this event object will be passed in as an argument.

User Actions are events associated closely with the DOM and use the same callback handler mechanism as many other programming environments, described by Gamma et al.'s (1995) Observer pattern. You'll see callback handlers used in other situations too:

onload and onunload are technically the same type of event as the User Actions described in this pattern because they work on DOM elements and use the same syntax. They relate to element lifecycles as opposed to user activity, which is why this pattern doesn't include them in the definition of User Actions. In most scripts, only the window (or document) onload is used; as this pattern shows, it's a good place to register User Actions, among other things. onunload is sometimes used to save data or confirm the user wants to quit.

The XMLHttpRequest Call (Chapter 6) pattern shows how you can register a callback to be notified of network activity as a remote call proceeds.

The Scheduling pattern shows how you can register a function to be executed at a certain time, or at fixed periods. There's no argument passed in, but the mechanism is similar to a DOM object event callback.


Let's say you want to run the following function when the user clicks a shutdown button:

  function shutdown( ) {
    if (confirm("Are you sure you want to shutdown?")) {
      postShutdownMessageToServer( );
    }
  }

The simplest way to set this up is to declare a button with an onclick event handler:

  <button id="quitButton" onclick="shutdown( );"/>Quit</button>
<!Obtrusive -->

Now the web browser will arrange for shutdown( ) to be called whenever the button is clicked. However, we can improve on this, because the above declaration mixes JavaScript with HTML. It's cleaner to just declare the button and deal with an event handler in a separate JavaScript file. Since we always want this behavior, we should declare it as soon as the page loads. To run something when the page loads, we can use another event handler that is triggered by browser activity rather than a User Action: onload.

  [HTML]
  <button id="quitButton"/>Quit</button>

  [Javascript]
  window.onload = function( ) {
    quitButton.onclick = shutdown;
  }

Note that we're declaring this inside window.onload instead of "out in the open"; if you do the latter, you might get an error because the script might be executed before the button is actually on the page. You'll see the window.onload idiom used in most JavaScript code, including all the Ajax Patterns demos.

Instead of referencing a callback function, it's sometimes convenient to define the callback as a closure (anonymous function), as in:

  quitButton.onclick = function( ) {
    if (confirm("Are you sure you want to shutdown?")) {
      postShutdownMessageToServer( );
      quitButton.onclick=null;
    }
  }

Registering events with JavaScript, as opposed to in HTML tags, is an example of unobtrusive JavaScript because it separates JavaScript from HTML. And defining the event handler in JavaScript also has another benefit: you can dynamically redefine actions in response to system events. Our shutdown( ) method could also redefine the handler to avoid a double shutdown:

  function shutdown( ) {
    if (confirm("Are you sure you want to shutdown?")) {
      postShutdownMessageToServer( );
      quitButton.onclick=null; // Quit button no longer
triggers an event.
    }
  }

Notice the model here involves a single handler for any event type; the above commands set the handler, in a manner that will remove any existing handlers. In most cases, that's just fine, and it has the merit of being completely portable. In some situations, though, it's nice to add a handler instead as it makes the code more modular. Two separate library functions can then register for the same events, without having to be aware of each other. Likewise, a function for removing would also be nice:

  addEvent(quitButton, "click", postShutdownMessageToServer);
  ...
  removeEvent(quitButton, "click", postShutdownMessageToServer);

Browsers do offer support for this functionality, but it's unfortunately varied, and a portable solution has been notoriously difficult. So much so that a competition was recently held to find the best addEvent( ) and removeEvent( ) functions, and you can find the winner, a 15-line script online (http://www.quirksmode.org/blog/archives/2005/10/how_do_i_create.html). Dojo Toolkit (http://dojotoolkit.org) also supports this behavior as part of its sophisticated event library.

It's not always enough for the event handler to know that an event has occurred; it also needs to know about the event. For example, the same event handler might be used for three different buttons, in which case it will need to know which of the buttons was clicked. For this reason, the web browser creates an event object upon each user event, containing various bits of information. In Firefox, it's passed to the event handler, so you just ensure an event parameter exists:

  function shutdown(ev) {
    ...
  }

In previous examples, we omitted the event parameter, which is just fine since parameters are optional in JavaScript functionsomitting them just means you don't get an opportunity to use them.[*] As it happens, IE doesn't pass the value in anyway, and instead holds the event in a window attribute. Again, JavaScript's loose handling of parameters means you won't actually get an error by including the parameter in IE. However, the value will always be null, which isn't very useful. What all this leads to is the following boilerplate code, which you can use whenever you care about the event. An "equalizer" statement gets hold of the event whichever browser we're in:

[*] Strictly speaking, you can still read all parameter values using the special arguments array.

  function shutdown(ev) {
    event = event || window.event;
    ....
  }

The event object contains various information, such as which element was clicked and where the mouse was. The various event types are covered next.

Decisions

What events will the script listen for?

Many events are made available to JavaScript code, and more come out with each new browser upgrade. Following are some frequently used and portable events, along with typical applications. Check the following out for more info on events: http://www.quirksmode.org/js/events_compinfo.html, and http://www.gatescript.com/events.html.

All handler functions accept a single parameter representing the event, and as discussed earlier in the "Solution," you have two options: ignore the parameter altogether (as in the initial shutdown( ) examples), orif you care about the event detailsinclude the parameter and equalize it (as in the shutdown(ev) examples above).


Key pressingonkeypress, onkeydown, onkeyup

  • onkeypress and onkeydown occur immediately after a key is pressed, and will also repeat if the key is held down. onkeyup is called just once, upon the key's release.

  • They can be used to enhance standard text editing. For instance, you can confine a phone number text field to contain only numbers, or you can show a word count while the user types.

  • They're sometimes used to automatically leave a field once it's valide.g., to proceed to the next field after five digits have been added to a zip code field. However, doing so is often counter-productive, as users generally perform faster when behavior is consistent, even at the expense of minor technical shortcuts.

  • They can be used to create keyboard shortcuts for custom controls. You can determine if the user's mouse is over the control with the onmouse* functions, and if so, respond to particular keypresses.

Which Keys to Use for Shortcuts?

Web browsers have their own keyboard events, such as Up and Down for scrolling and the popular Ctrl-D for bookmarking. It's best to avoid using thesethey'll confuse users if the browser allows them or simply fail to work if the browser forbids them. In addition, keyboard shortcuts might also conflict with operating system shortcuts such as those for cut-and-paste. This effectively means avoiding all standard shortcuts on all major operating systems. For all these reasons, the most common shortcuts are plain old keysletters, numbers, and symbols, with no modifiers. However, that won't work while the user's editing text, so if that happens a lot, you'll need to be inventive and perhaps offer some flexibility.



Keyboard focusonblur, onfocus

  • In the case of editable fields, onblur indicates keyboard focus has been lost, suggesting an update has probably occurred, so is often used to initiate a remote call or some validation technique.

  • onfocus suggests the user has begun working on a particular object, so it can be used to show online help or change the display inside a Status Area.


Mouse button clickingonmouseup, onmousedown, onclick, ondblclick

  • onclick and ondblclick indicate a button has been clicked or double-clicked. onmousedown and onmouseup indicate a button has been depressed or released. These latter events are more fine-grained than clicking, which implies the sequence of mousedown followed by mouseup has completed, both on the same element (note that click won't fire if the user releases the mouse button over a different element). The button control is specifically geared for catching click events to let the user do something, and radiobuttons and checkboxes can also be associated with click listeners to indicate changes.

  • onmousedown and onmouseup can be used for panning behavior and for custom drag-and-drop functionality.


Mouse movementonmouseover, onmouseout

  • onmouseover and onmouseout indicate the mouse has just moved over, or has just left, an element. It can be useful to keep a pointerElement variable to track which element is currently selected.

  • They can be used to change an element's style when the mouse rolls over it. This shows it's active and can convey that this is the element that will be affected if the user clicks the mouse button right now, or perhaps hits a certain key.

  • They can also be used to provide help or further information in a Status Area or a Popup.


Selectiononselect

  • onselect indicates when the user has selected some text.

  • By tracking the selection, the application can provide information based on what the user's selected. For example, you could let the user search on a term by selecting it, and then morph the Search Results element.

  • By tracking the selection, the application can also allow transformations to occur. For example, the textarea in many modern content management applications, such as mediawiki, allows the user to select some text and then change it, just like in a word processor. To italicize text on Wikipedia, select the text and click the <i> icon, which then wraps mediawiki markup ('') around the selected text.


Value changeonchange

  • onchange indicates a value has changed, so it's often used to initiate a remote call or some validation technique. This is an alternative to onblur. Unlike onblur, it is only called if the value is actually altered.

What attributes of the event will be inspected?

The event object contains several useful pieces of information about the event and what was going on at the time. Note that some of these attributes are set even for events you may not expect. For example, the ctrlKey modifier will be set even for a mouse-click event. This would allow you to detect a Ctrl-mouse press action. However, not all attributes are always set, so you need to be careful in testing for portability.

Following are some of the portable and more frequently used attributes of the event object:


Elementtarget (Firefox), srcElement (IE)

  • target and srcElement indicate which element the event occurred on. To equalize across browsers:

      el = ev.target || ev.srcElement
    

    This is useful if you have a single function listening to lot of elementsfor instance, an e-commerce itemClickListener monitoring all items for a click event. Inspecting this property will tell it which particular item was clicked.


Event Typetype

  • type indicates which event type took place; e.g., click.

  • This is a potential code issue, because it suggests the same function has been configured to handle multiple events. If it then needs to distinguish among the different types of events, it might be worth breaking it out into a handler function for each event type, with any common routines placed elsewhere.


Key codewhich (Firefox), keyCode (IE)

  • which and keyCode indicate the Unicode value of the key that was pressed.[*] This isn't completely consistent across browsers but is easy enough to equalize. Since you can't directly register a function against a specific key, this property is the only way to decide if a certain key was pressed.

    [*] Find character codes with the Unicode chart at http://www.macchiato.com/unicode/charts.html.


Key modifiersaltKey, ctrlKey, shiftKey

  • The altKey, ctrlKey, and shiftKey are modifiers indicating if the special keys Alt, Ctrl, and Shift were being held down while a key event occurred. You can use the modifiers to introduce keyboard shortcuts to the application. Since many single-modifier shortcuts are already used by one browser or another, portable applications often need to use double-modifiers. Thus, the key-handling function will need to perform a check like:

      if (ev.ctrlKey && ev.shiftKey) {
        ... // perform ctl-shift shortcut
      }
    

  • There is also a meta-key modifier, which is generally not advisable as it's not supported by IE, and in any event, available only on certain keyboards.


Mouse buttonsbutton

  • This indicates which mouse buttons were being associated with the event. In IE, 1 is left, 2 is right, and middle is 4. The value represents the sum of all buttons being pressed, allowing you to catch "chords"multiple keys held down at once. In Firefox, 0 is left, 1 is middle, and 2 is right.

  • This is a painful area due to serious incompatibility issues (http://www.quirksmode.org/js/events_compinfo.html). As well as the differences above, beware of incompatibilities when one button is being depressed while another is already depressed, and also incompatibilities in which events provide this property (sometimes only mouse clicks; sometimes others).


Mouse positionclientX, clientY

  • These indicates the position of the mouse pointer when the event took place, relative to the browser window.

  • This is useful for image-based applications, such as maps and simulators. It's often not practical to register event handlers here, so JavaScript codewith possible help of web remotingcan determine exactly what the user clicked on by examining the coordinates.

Will event handlers be registered after the page has loaded?

Using JavaScript and the DOM, redefining event handlers is easy enough to do, but should you do it? Redefining the effect of user events must be done with caution, as there is great potential to confuse users. Sometimes, event redefinition occurs simply because the programmer can't be bothered adding a new control, or the UI is so small that designers want to reuse an existing control. So before deciding to redefine an event, ask yourself if there are alternatives. For example, could you add a second button instead of redefining the first button's action?

A few examples where event redefinition might be worthwhile:

  • For state transitions. The JavaScript may have separate start( ) and stop( ) methods, and you need a toggle button to flip the state, since that is clearer and less error-prone than separate "on" and "off" buttons.

  • For enabling and disabling. There is already disabled a available for standard controls (http://www.quirksmode.org/js/disabled.html), but for custom controls that you may have created, you might use event redefinition to cancel or re-enable the effects of interacting with the control.

  • For actions that depend on dynamic information, such as which field has input focus.

However, in all of these cases, it's usually simpler to have a single method, always registered in the same way, and to allow that method's JavaScript to decide where to route the event.

Real-World Examples

Google Reader

Google Reader (http://google.com/reader) is a web-based RSS aggregator (Figure). You can change the current article by mouse-clicking on article titles. An interesting feature is keyboard shortcutswhen the page contains numerous articles, clicking "j" and "k" will scroll up and down to the previous or next story.

Google Reader


Google Maps

Google Maps (http://maps.google.com) uses a dragging action to pan the map within a Virtual Workspace, and the arrow keys can also be used.

Backpack

37Signals' Backpack (http://www.backpackit.com/) maintains items in a list and illustrates how you can use Drag-And-Drop in an Ajax App. Drag-And-Drop relies on monitoring the mouse button as well as position.

Code Example: Basic AjaxPatterns Demos

Here are a couple of basic examples from the Ajax demos. The Basic Time Demo (http://ajaxify.com/run/time) handles a button click like this:

  $("defaultTime").onclick=requestDefaultTime;

The wiki tracks that focus and blur events in order to show the user which message is being edited and to upload any messages after a blur occurs. It also tracks mouse movement over each area, to provide an affordance indicating that the fields can be edited:

  messageArea.onmouseout = onMessageMouseOut;
  messageArea.onmouseover = onMessageMouseOver;
  messageArea.onfocus = onMessageFocus;
  messageArea.onblur = onMessageBlur;

Each of these passes to getMessage, which identifies the message element that was acted upon:

  function getMessage(event) {
    event = event || window.event;
    return event.target || event.srcElement;
  }

Alternatives

"Click 'n' Wait"

The conventional web app follows the "click 'n' wait" pattern, popular in 1970s mainframe-based client-server applications and revived in time for the late-1990s web generation, albeit in color. The only type of interactivity is the user submitting a static form to a server-side CGI script or clicking on a link. The script then reads some variables, does something, and outputs a whole new page of HTML. A full page refresh once in a while is OK, when a big context switch takes place, but basic updates are best controlled with JavaScript.

Richer forms

The "richer form" is richer than static HTML, but less so than Ajax. It involves enhancing a standard form with dynamic behavior, so as to make things clearer and help prevent the frustrating validation errors that often come back from the server. For instance, DHTML can be used to ensure a user enters only digits into a credit card field or to add some pop-up instructions for a form field.

Related Patterns

Display Morphing, Page Rearrangement

Display manipulation, as discussed in Display Morphing and Page Rearrangement (Chapter 5), is often triggered by User Events.

XMLHttpRequest Call, IFrame Call

Web remoting, as discussed in XMLHttpRequest Call and IFrame Call (Chapter 6), is often triggered by User Actions.



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows