For years, those who design and implement complex behavior in event-driven programs have used finite state machines as an organizing principle. Now, programmable Web browsers have opened a new event-driven environment to a new generation of applications. As browser-based applications, popularized by Ajax, become more complex, designers and implementers can benefit from the discipline and structure that finite state machines offer.
Part 1 of this series described a tooltip widget for Web pages with more elaborate behavior than the built-in implementation provided by popular Web browsers. This behavior requires the FadingTooltip widget to respond to a variety of different events. Sometimes the desired response to an event depends upon previous events. You used the finite state machine pattern to design this behavior. The resulting state graph and table representations indicate the actions to take in response to each event in all situations. You also compiled a list of variables to remember between events to take related actions.
Part 2 translated your design into JavaScript, taking full advantage of function closures and associative arrays. The implementation accommodates the quirks of popular browsers without sacrificing efficiency or elegance. You implemented code to: hook cursor events for all three browser event models, start and cancel both types of timers, and hook their timer events. You implemented your state table as a common handler for all events, plus a function array for all actions and transitions. The tooltip is a fully parameterized HTML Division element that moves and fades in response to cursor and timer events, as specified in the state table.
In this final article you'll test the implementation in some popular browsers. You need to construct a simple test page that creates some FadingTooltip widgets and binds them to HTML elements. For comparison, your test page will also illustrate some built-in tooltips. Immediately you will encounter some should-not-occur conditions, and so will have the opportunity to see how gracefully the design adapts. This article concludes with some observations about performance, and ideas for further development of finite state machines for browser-based applications.
Running applications in browsers
Ideally, you should test applications in all of their likely execution environments. For JavaScript applications, this is a daunting task given the variety of browsers available and the number of versions in widespread use. Because the FadingTooltip widget is only a technology demonstration, and won't be used beyond this series, I only tested with the current versions of four popular browsers:
- Netscape Navigator 8.1
- Microsoft® Internet Explorer® 6.0
- Opera 9.0
- Mozilla Firefox 1.5
This widget was only tested with the simple harness described in the following section. Any production application would warrant more comprehensive testing.
One straightforward way to test your implementation is with some
code embedded in an HTML Web page. The code must create
FadingTooltip objects with the constructor, and bind them to HTML
elements. A simple way to do this is with a function, defined in the HTML head element of a Web page, that uses
the id attributes of HTML elements, as shown in Listing 1.
Listing 1. JavaScript code for creating FadingTooltip widgets
<head>
...
<script src='FadingTooltip.js' content='text/javascript'></script>
<script content='text/javascript'>
function createFadingTooltip(id, content, parameters) {
new FadingTooltip(document.getElementById(id), content, parameters);
}
</script>
...
</head>
|
The arguments for the createFadingTooltip function are
an HTML element identifier, the content of the tooltip, and an
optional set of parameters. The function simply converts the element
identifier into a pointer and then calls the constructor, passing
the remaining arguments unchanged. The pointers to the objects
returned by the constructor are discarded, since the constructor
encloses pointers to the objects
with the event functions it defines, as described in Hooking Cursor Events in Part 2.
Next, you need some HTML elements with id attributes, defined in the HTML body element of a Web page, as shown in Listing 2.
Listing 2. HTML code for some sample HTML elements
<body>
...
<p>These elements have tooltips defined with the FadingTooltip widget:
<div id='tests' class='TestStyle'>
Here are some <span id='TestLabel'>more elaborate tooltips</span>:
<input type='text' id='TestInput' size=25>
<input type='button' id='TestButton' value='Press this button'>
</div>
...
|
Finally, you need some code that calls the createFadingTooltip
function with appropriate arguments for each HTML element, as in Listing 3.
Listing 3. JavaScript code to bind FadingTooltip widgets to HTML elements
<body>
...
<script content='text/javascript'>
createFadingTooltip('TestLabel',
'Move your cursor a bit to the right, please ...');
createFadingTooltip('TestInput',
'Type the following, <i>please</i>:' +
'<ul compact style='margin-top: 0; margin-bottom: 0'>' +
'<li>your bank account number' +
'<li>your PIN number' +
'</ul>' +
'<i>Thank you</i> in advance.',
{ fadeinTime: 3,
fadeoutTime: 3 } );
createFadingTooltip('TestButton',
'<img src='smiley.gif' align='absmiddle'>' +
'<big>Go ahead.</big> ' +
'Press it. ' +
'<small>What's the harm? <small>Trust me.</small></small>',
{ tooltipOpacity: 1,
tooltipClass: 'AnotherTestStyle',
pauseTime: 2,
fadeinTime: 0.5,
displayTime: 0,
fadeoutTime: 10,
trace: true } );
/script>
...
|
The first tooltip, for the HTML element identified as TestLabel,
is created with the simplest of arguments: the content is text only,
and the parameter argument is omitted, so default values will be used
for all parameters. The second tooltip, for the TestInput
HTML element, formats the content with some markup, and specifies the
fade in and fade out times (in seconds).
The third tooltip, for the TestButton HTML element,
includes an image in the formatted content, and specifies more
parameters, including a Cascading Stylesheet (CSS) class to style the tooltip.
Mozilla Firefox is the newest and trendiest browser available, and adheres more closely to open standards than some others, so we will start with it. (If you use Microsoft Internet Explorer, you might want to skip ahead now and read the next two sections, about should not occur and fading doesn't work, and then return here.)
Try the test harness now. It contains some HTML elements with built-in tooltips, and the FadingTooltip examples described above, so you can compare and contrast them. Note the more elaborate behavior of the FadingTooltips. They fade into and out of view, rather than popping in and out, and the fading reverses direction as the cursor moves over and away from the HTML elements. They follow the cursor's movement, and do not disappear when keyboard events occur. They have a more elaborate appearance: FadingTooltips are styled, their text is formatted, and they can contain images.
Decide for yourself whether these differences are improvements, and whether they are worth the time and effort you invested in developing your own tooltip widget. If so, you might want to copy the source files for the implementation and test harness onto your disk drive so you can alter the parameters, styles, or code (see Download). You don't need a Web server of your own for this; just use the file://... URL to load your modified test harness into your browser.
When should-not-occur events occur
Microsoft Internet Explorer is by far the most widely used browser, so you have to test the implementation with it as well, and it doesn't take long to find a problem. An alert, like shown in Figure 1, appears immediately when the cursor passes over any of the HTML elements with FadingTooltip widgets.
Figure 1. An unexpected mousemove event in Internet Explorer
Refer to the state table from Completing the state table in Part 1, reproduced below in Figure 2. Recall that we
did not expect mousemove events in Inactive
state, so we left this cell empty in the implementation of the
actionTransitionFunctions table in Creating the action/transition table in Part 2.
Figure 2. Initial state table for FadingTooltip widget
Intuitively, a mouseover event should
precede a mousemove event, causing a transition to
Pause state, which does expect mousemove
events (see Figure 5 in Part 1). Evidently, Firefox works as your
intuition expects, but Internet Explorer does not. Perhaps there is a bug in a finite state machine within Internet Explorer that generates cursor events, or perhaps that browser does not keep track
of its state internally. In any case, you'll have to
accommodate this situation in your own finite state machine.
Fortunately, the discipline required to apply the finite state machine pattern
pays off now by making changes like this easy. Consider what
behavior is needed should a mousemove event occur in
Inactive state: the actions and transition are the same
as for a mouseover event in Inactive
state. Referring back to your implementation of the
actionTransitionFunctions table, remember that the doActionTransition method allows any function in the table to duplicate the actions and
transitions of another function. Using that method, you can accomodate this unexpected event
by adding the function highlighted in bold in Listing 4 to the
actionTransitionFunctions table.
Listing 4. JavaScript code to handle an unexpected mousemove event in Inactive state
FadingTooltip.prototype = {
...
actionTransitionFunctions: {
...
Inactive: {
mousemove: function(event) {
return this.doActionTransition('Inactive', 'mouseover', event);
},
...
|
Further testing with Internet Explorer will soon reveal another similar unexpected situation, as shown in Figure 3.
Figure 3. An unexpected mouseout event in Internet Explorer
Again, intuition led you to expect that mouseover events would precede mouseout events, so the mouseout events would not
occur in Inactive state. Fortunately, this unexpected event is also
easy to accommodate. No actions or transition are needed in this
situation; you just want the finite state machine to ignore mouseout
events in Inactive state, as shown in Listing 5.
Listing 5. JavaScript code to handle an unexpected mouseout event in Inactive state
FadingTooltip.prototype = {
...
actionTransitionFunctions: {
...
Inactive: {
mouseout: function(event) {
return this.currentState; // do nothing
},
...
|
In the design phase, you did not anticipate that mousemove
or mouseout events would occur in Inactive
state. But before criticizing Microsoft for the mouseover
events in their browser, let's imagine what would happen with any
browser in this situation: the cursor moves over an HTML element and
pauses there long enough for the FadingTooltip widget to fade into
view, display for a while, and then fade from view. This would cycle
your finite state machine through each of its states, returning to
Inactive state with the cursor still over the HTML element. When the cursor then moves, any browser will generate a
mousemove or mouseout event in Inactive
state. This does in fact occur in other browsers, including Firefox.
Independent of any possible bugs in Internet Explorer, there is a
design defect in our finite state machine, as shown in Figure 4.
Figure 4. An unexpected mousemove event in Firefox
Fortunately, the changes for Inactive state that you have already implemented for Internet Explorer handle this situation correctly, so no additional changes are needed to cover thr design defect.
Unfortunately, continued testing after implementing these changes reveals that one more should-not-occur situation occurs with Internet Explorer: an unexpected mouseover event in Pause state.
Since
mouseover events in Pause state need the
same actions and transition as mouseover events in
Inactive state, you can handle this situation by calling
the doActionTransaction method. However, there is no
legitimate sequence of events (which I can think of) that can lead other
browsers into this situation, so let's implement this design change only for Internet Explorer, as in Listing 6.
Listing 6. JavaScript code to handle an unexpected event only in Internet Explorer
FadingTooltip.prototype = {
...
actionTransitionFunctions: {
...
},
...
};
if ( (window.navigator.userAgent).indexOf('MSIE')!=-1 ) {
FadingTooltip.prototype.actionTransitionFunctions["Pause"]["mouseover"] =
function(event) { return
this.doActionTransition('Inactive', 'mouseover', event);
};
}
|
If the browser is some version of Internet Explorer, modify
the actionTransitionFunctions table of the
FadingTooltips prototype, after it has been defined but
before it is used, to handle mouseover events in
Pause state the same way as mouseover
events in in Inactive state. And remember that in JavaScript, associative arrays and objects are equivalent, so either notation can be used to modify the table.
When fading tooltips don't fade
Unfortunately, after all of the alerts about unexpected events
have been eliminated by the design changes in the previous
section, continued testing with Internet Explorer reveals another
unrelated problem. Tooltips defined with the FadingTooltip widget pop
into and out of view, rather than fading in and out. Tragically,
Internet Explorer does not support the proposed standard CSS
opacity style (see Resources).
Internet Explorer does support a non-standard style named filter
that has a similar capability (see Resources). To employ it, you need to modify the createTooltip method by inserting the lines highlighted in bold in Listing 7.
Listing 7. Additional JavaScript code to create tooltip in Internet Explorer
FadingTooltip.prototype = {
...
createTooltip: function() {
this.tooltipDivision = document.createElement('div');
...
this.currentOpacity = this.tooltipDivision.style.opacity = 0;
if (this.tooltipDivision.filters) { // for MSIE only
this.tooltipDivision.style.filter = 'alpha(opacity=0)';
}
...
},
...
|
A corresponding insertion into the fadeTooltip method is also
needed, as shown in Listing 8.
Listing 8. Additional JavaScript code to fade tooltip in Internet Explorer
FadingTooltip.prototype = {
...
fadeTooltip: function(opacityDelta) {
...
this.tooltipDivision.style.opacity = this.currentOpacity;
if (this.tooltipDivision.filters) { // for MSIE only
this.tooltipDivision.filters.item('alpha').opacity =
100*this.currentOpacity;
}
},
...
|
On a lighter note, Opera might have limited brand-name recognition as a browser, but is well regarded in technical circles. Unfortunately, Opera was comparatively late in supporting the CSS opacity style, so FadingTooltip widgets will also pop in and out of view, rather than fading in and out, with versions of Opera prior to release 9. Unlike Internet Explorer, earlier versions of Opera have no alternative syntax for transparency; the only solution is to upgrade to the current version.
A few more words about performance
With your FadingTooltip widget now working smoothly in popular browsers, it's time to find out how close you came to the performance goal of negligible processor usage. An easy way to do this on Windows is to watch the Performance panel of the Windows Task Manager while running the widget's animations.
On most workstations, usually several programs always run quietly in the background, even when you've not started any applications. Some of these programs drive the small icons near the clock on the Windows taskbar; others have no visible manifestation at all. The Performance panel of the Windows Task Manager, however, shows their activity, as shown in Figure 5.
Figure 5. Windows Task Manager showing normal background activity
Before running the FadingTooltip widget, you can eliminate some of this background activity so that it doesn't obscure the widget's processor usage. You can close most programs that display icons in the Windows taskbar from their context menus. You can stop other background programs from the Windows Control Panel for Services.
On a quiescent system with a 1.1GHz Intel Pentium-III processor, the processor usage of the FadingTooltip widget does indeed seem to be negligible, as shown in Figure 6.
Figure 6. Windows Task Manager showing widget animation activity
Because the widget's animation places little demand on the processor, we can be confident that it will run smoothly even when the processor is heavily loaded with other activity.
Don't forget to update that design documentation
Now that you are finished testing your implementation, you need to update the documentation to reflect the design and implementation changes you made. The unexpected events you encountered correspond to empty cells in the state table, so you need to update those cells (highlighted in blue) with the actions you took to accommodate those situations, as shown in Figure 7.
Figure 7. State table for FadingTooltip widget, as tested
As shown in Figure 8, the matching updates to the state graph are equally simple.
Figure 8. State graph for FadingTooltip widget, as tested
The lines of code you inserted into the createTooltip and fadeTooltip methods to accommodate Internet Explorer's
way of styling opacity don't really rise to the level of a design change. You
will document this in the source commentary (see Download).
The purpose of this series is to demonstrate how to apply the finite state machine design pattern to browser-based applications, and to show how to exploit two distinctive features of the JavaScript language to produce an elegant, efficient program. Consequently, you developed the FadingTooltip widget as a single self-contained JavaScript object. The code is tight, but it is not very flexible. Before I close this discussion, I want to consider some possible directions that further development might take.
Other visual widgets could benefit from appearance and behavior similar to the FadingTooltip widget. For example, you might display syntax error messages for input field typing mistakes, suggestions for filling in complex forms, or guidance through unfamiliar workflows in styled tooltip-like boxes that fade into view when events dictate, move with the cursor, and eventually fade from view when events warrant. One possible next development step might involve refactoring the FadingTooltip code to move the basic machinery of the finite state machine into a separate object. This might include the event hooks, the event handler, the error methods, and the timer methods. Then you could implement a family of visual widgets, including the FadingTooltip widget, as separate objects that inherit the data and methods of the basic object and add the state variables, transition tables, and action methods needed for each type of widget.
Tooltip-like widgets and other visual widgets might benefit from additional animation effects similar to fading. For example, tooltips might slide into view from an edge of the window, or expand as though zooming in from a vanishing point, or unfold into view from an icon like origami in reverse, or shimmy like Jell-O as the cursor moves them. Any of these animation effects might be as effective with context menus, dialog boxes, and input prompts as they would be with tooltip-like widgets. Another possible next development step might involve building a framework for visual widgets that further separates the finite state machines for each type of animation, and the behavior of each type of widget, into separate objects so that any combination can be bound to a particular HTML element.
Of course, we have not considered several other types of events in these articles, such as those arising from the keyboard or the network. For example, the individual characters typed into a text input field might trigger actions that validate values as they are entered, or display the remaining valid values in a selection list, based on a formal grammar represented as a finite state machine. Network requests that fail or time out might be handled by retrying alternate services represented as protocol graphs. Another possible next development step might involve:
- Extending a framework for finite state machines to include hooks for keyboard and network events
- Graph or table representations for formal language grammars and network protocols
- Actions to validate text values and manage network sessions
Other applications running on the same processor can originate
events and expose actions that are of interest to programs running in
browsers. For example, a voice-over-IP telephony
application might generate ring-in, ring-out,
connect, and disconnect events, and
expose call, hold, and hang-up
actions. Yet another possible next development step might involve
extending a framework to include methods to hook events in other
applications and take actions in them. This might require some Java™
plug-ins to make events and actions in other processes accessible to
finite state machines running in browsers. And that would be a very
satisfying place to end, since JavaScript began as a scripting
language for Java plug-ins.
- Demo: Examples of browser tooltips and FadingTooltip widgets
- Code sample: HTML source code for examples of browser tooltips and FadingTooltip widgets
- Code sample: JavaScript source code for FadingTooltip widget
Learn
- Ajax: A New Approach to Web Applications: Read Jesse James Garrett's article that coined the term Ajax.
- The book
JavaScript: The Definitive Guide (David Flanagan, published repeatedly by O'Reilly Media between 1996 and 2006): Find exhaustive information on how JavaScript works in browsers.
- The Standard ECMA-262: ECMAScript Language Specification (Ecma International, 1999) : Peruse the authoritative definition of the JavaScript language implemented by popular browsers.
- W3C Cascading Style Sheets
Under Construction: Follow the CSS3 development and find a rough schedule for CSS WG (Cascading Style Sheets Working Group, formerly "CSS & FP WG") activities.
- MSDN Alpha Filter: Adjust the opacity of the content of an object.
- Document Object Model (DOM) Level 2 Events Specification (W3C, 2000): Refer to this spec for the authoritative definition of the DOM Level 2 event model.
- The Gecko DOM Reference (Mozilla): Get the authoritative definition of the object interfaces, including events, implemented by the Firefox browser.
- HTML and DHTML Reference (Microsoft): Refer to the authoritative definition of the object interfaces, including events, implemented by the Internet Explorer browser.
- Chapters 21 "Protocol Representation with Finite State Models" by Andre A. S. Danthine, and 25 "Executable Representation and Validation of SNA" by Gary D. Schultz, et. al. in Computer Network Architectures and Protocols (edited by Paul E. Green, Jr., Plenum Press, 1982): Read historic examples of finite state machines applied to computer network protocols.
- Chapter 3.5 "Finite Automata" in Compilers: Principles, Techniques, ad Tools (Alfred V. Aho et. al., Addison-Welsley, 1986): Read a description of how finite state machines are applied to computer language compilers.
- Chapter 5 "Behavioral Patterns" in Design Patterns: Elements of Reusable Object-Oriented Software (Erich Gamma et. al., Addison-Welsley, 1995): Read about the State pattern for a discussion of implementing finite state machines.
- Chapter 13.25 "TCP State Machine" in Internetworking with TCP/IP (Douglas E. Comer, Simon and Schuster Company, 1995): Read to understand the finite state machine that underlies the Internet.
- Chapter 15 "State Machines" in the Unified Modeling Language 2.0 Superstructure Specification (Object Management Group, 2004): Read for a complete graph representation of finite state machines.
- Chapter 15 "State Machines" in the Unified Modeling Language 2.0 Superstructure Specification (Object Management Group, 2004): Read for a complete graph representation of finite state machines.
- developerWorks Web development zone: Expand your site development skills with articles and tutorials that specialize in Web technologies.
- The technology bookstore: Browse for books on these and other technical topics.
- developerWorks technical events and webcasts: Stay current with jam-packed technical sessions that shorten your learning curve, and improve the quality and results of your most difficult software projects.
Get products and technologies
- developerWorks Web development Downloads and products area: Find more free downloads.
- Download the following browsers to test the FadingTooltip widget:
- Mozilla Firefox browser
- Microsoft Internet Explorer browser
- Netscape Navigator browser
- Opera browser
Discuss
- developerWorks blogs: Get involved in the developerWorks community!

Edward Pring holds an M.S. degree in Computer Science from New York University and a B.S. degree in Mathematics from Stanford University. As part of IBM Research, he has contributed to a wide range of IBM products and technologies, including operating systems, publishing applications, terminal emulators for mainframes, virus protection for personal computers, network automation for the Digital Immune System, and visualization and performance analysis for Web Services. His patent portfolio spans all of these fields.




