| | JavaScript DHTML Utilities: Dynamic HTML Without the HassleBy Michael Bostock Netscape Production Engineer | | |
The utilities are divided up into three main components. The first is
"behaviors." Behaviors define how layers interact with the user. For example,
is the layer draggable? What happens when the user clicks on the layer, or
puts her mouse over it? The second component is "motion." This lets you
quickly and easily make moving elements - either transitions, or layers
that fly across the page. Finally, the utilities let you dynamically load
and play sounds without having to understand Netscape's LiveAudio. Together these three
components can make it easy to develop compelling DHTML content.
Contents
How to Use the DHTML Utilities
Before you do anything, you need to understand layers. Whether you use
the W3C standard
Cascading Style Sheets or the depreciated <LAYER> tag, you can't have
Dynamic HTML without the layers. To learn about Dynamic HTML, check out
Netscape's Web Building Studio
Web site for content developers.
Including The Library Files
The first step to using the utilities is to include them into your HTML document.
Including the DHTML Utilities is a simple process; all you need to
do is add the following code somewhere inside
the head of the document (between the <HEAD> and </HEAD>):
View the code.
(Alternatively, you can download the files
[behavior.js,
motion.js, and
sound.js]
to your own hard drive by right-clicking on the links, choosing "Save Link As..."
and then using the libraries locally.) Of course, if you only want to use
part of the utilites (the Behavior Object, e.g.), you only have to include the .js
file for that specific object (behavior.js).
The Behavior Object
The Behavior Object lets you provide interactivity to the elements on your Web page without
having to worry about the JavaScript Event Model. Futhermore, it lets you create a behavior
and easily apply it to multiple layers. A behavior is a collection of event-action pairs
that can be applied to (associated with) a layer. Events are anything the user does to
interact with the Web page, from clicking the mouse to pressing a key.
The DHTML utilities let you assign actions (functions) for each of those events.
Finally, because draggable layers are so commonly used, the Behavior Object has draggability built-in.
Creating A Behavior
To use the Behavior Object, you first have to create one. Use the Behavior's constructor
function, like so:
myBehavior = new Behavior();
(Optionally, you can pass the constructor true
as an argument, and the resulting Behavior Object that is created will be draggable.)
After the Behavior has been created, it's up to you to define what it does. To do this,
you use the setAction() function.
The setAction() function takes
two arguments. The first is the type of event you want to handle, and the second is what
action you want to take place (the name of a function to be called on the event).
Here are the different values that can be passed as the first argument to
setAction():
Argument |
Plain English Equivalent |
"MOUSEDOWN" |
The user puts the mouse down on the layer. |
"MOUSEUP" |
The user releases the mouse on the layer. |
"MOUSEMOVE" |
The mouse is moving over the layer. |
"MOUSEOVER" |
The mouse enters the layer area. |
"MOUSEOUT" |
The mouse leaves the layer area. |
"CONTAINERPUSH" |
(See "Layers As Containers.") |
"CONTAINERPULL" |
(See "Layers As Containers.") |
N.B.: the "MOUSEMOVE" event is independent of whether the mouse button
is pressed or not. "MOUSEOVER" and "MOUSEOUT" are generally used in combination
to create "roll-over" effects. (If you've ever seen an image change when you put the mouse
over it, you know what a roll-over effect is.)
A Simple Example
Here is an example of a useful behavior:
myBehavior = new Behavior();
myBehavior.setAction("MOUSEOVER", hilite);
myBehavior.setAction("MOUSEOUT", unhilite);
myBehavior.setAction("MOUSEUP", start);
myBehavior.applyBehavior(myLayer);
You could then define the roll-over functions like so:
function hilite() {
if(this.background) this.background.src = "red.gif";
else this.style.backgroundImage = "url(red.gif)";
}
function unhilite() {
if(this.background) this.background.src = "blue.gif";
else this.style.backgroundImage = "url(blue.gif)";
}
This would switch the background image of the layer to "red.gif" when the mouse goes
over, and returns it to "blue.gif" when the mouse leaves. (If you want tools to help
you set layer properties [CSSP] from JavaScript without understanding the
differences between the two browsers' implementation, check out a
cross-browser API that Netscape created.) When the user clicks on the
layer, start() - or whatever
function you want - would then perform some action (say, showing a layer or starting
an animation). Here's that same example, in action:
Updating a Behavior
Once you've created a behavior, at some point you'll probably want to change attributes
of that behavior, even after you've applied that behavior to a layer. Updating a behavior
is as easy as defining it. To change an action associated with an event, simply use
the setAction() function; this will
override whatever was set before. If you want to get rid of an action, pass
null as the second argument of
setAction().
Once the behavior has been changed, it will only take effect on whichever layers you
call applyBehavior().
If you want to change a behavior's draggability, all you have to do is set the behavior's
draggable attribute
(for example: "myBehavior.draggable = true;").
One of the advantages of defining behaviors instead of hard-coding them yourself
is the ability to make multiple layers behave in the same way. Once you've defined the Behavior Object,
just call applyBehavior()
for each layer you want to be associated with that behavior.
Then, updating behaviors of layers becomes even easier. If you change
a Behavior's attribute, you can update all the layers associated with that behavior.
To do this, use the update() function.
("myBehavior.update();", e.g.) Of
course, you can continue to use applyBehavior()
if you only want to update a specific layer, and not all layers associated with
the behavior.
Layers As Containers
Another useful feature of the Behavior Object is the ability to define containers for a
layer. Here's an analogy to explain. Imagine one layer representing a Crayola crayon
box, and another layer representing a Burnt Sienna crayon. It is frequently useful to
know when the user removes the crayon from the box (when the user "pulls" the layer
outside of the container layer) or puts the crayon in the box ("push"). To use the container
functions, you first define different containers for the Behavior. (The Behavior can have
multiple containers.) Do this using the
addContainer() function, with
the only argument being the layer you want to function as a container. Next, you set
an action for the Behavior, either "CONTAINERPUSH" or "CONTAINERPULL". Here's an example.
myBehavior = new Behavior(true);
myBehavior.addContainer(myLayer);
myBehavior.setAction("CONTAINERPUSH", hello);
myBehavior.setAction("CONTAINERPULL", goodbye);
Thus, when the user drags a layer with the behavior myBehavior onto the
layer "container", hello()
is called. If the user then drags the layer out of the container,
goodbye() is called. These
two functions are passed an argument which represents the container the layer
was pushed on to or pulled from. (And "this" for those two functions refers to
the layer being moved by the user.)
Here's a similar example in action:
You can also remove containers from the behavior, using the
removeContainer() function.
Additional Features
You can set certain restrictions on how a layer is draggable. Two attributes of the
Behavior Object, hLock and
vLock, accept boolean values
and determine if the layer can be moved horizontally and moved vertically, respectively.
The default value is false. You can also specify a range that the
layer can be dragged in (a bounding rectangle of which the layer cannot be dragged outside).
To do this, use the function
setBounds(), with the arguments
being the left, right, top and bottom position of the bounding rectangle (in that order).
The example above used setBounds()
like so:
myBehavior.setBounds(0,500,0,64);
In addition to handling events for you, the DHTML utilities has three simple functions
to tell if a modifier key (shift, alt, or control) was depressed on an event. These
three functions are
isShift(e),
isAlt(e), and
isControl(e).
They return a boolean value. Pass them the same argument that is passed to the action
associated with an event. (For instance, the
hilite(), function above was
passed the event but ignored it. To access the event, change the prototype to
function hilite(e). You could
then use isShift(e) to see if
the shift key was down.)
For a first-hand experience of what the Behavior Object is capable of, check out this
interactive demo.
The Motion Object
Aside from adding interactivity to layers, the other most common "cool thing" people want
in DHTML is motion. Motion typically comes in two forms: transitions and movement.
Transitions involve making a layer visible or invisible gradually. Movement means moving a
layer from one position to another gradually. Unfortunately, the way to go about animating
such motions is not always simple - especially if you want to do several motions in
sequence. (JavaScript makes it much easier to do animations in parallel rather than in
sequence.)
The Motion Object supports both transitions (in a variety of forms) and movement. It
also provides an easy way to do multiple motions in sequence, or in parallel.
Creating A Motion Object
To use the Motion Object, you first have to create one. Use the Motion Object constructor,
and 3 arguments:
myMotion = new Motion(direction, ex, length);
The first argument, direction,
applies to what direction the motion is in. For movement, the value could be anyone
of "H", "V" or "HV". Here "H" stands for horizontal, and "V" stands for vertical.
For transitions, the value is "L", "R", "U", "D", or any combination
or those letters. (They stand for left, right, up and down, respectively.)
Since the Motion Object can be used for both transitions and
movements, you can combine acceptable values for both transitions and movements.
For instance, "HVLRUD" would build a Motion Object with motion in all directions.
The second argument, ex, is also
a boolean value. It specifies whether the motions are exclusive. If
ex is true, the motions happen
in sequence. If false, the motions happen in parallel.
The final argument specifies the approximate time you want the motion to take in
seconds. Partial seconds (2.34, e.g.) are acceptable.
Using the Motion Object: Transitions
Once you've built the Motion Object, to perform a transition on a layer, all you have
to do is use the applyTransition()
function. Here's the syntax:
myMotion.applyTransition(layer, fadein);
The second argument, fadein,
is a boolean. If true, it specifies a transition that brings a layer into visibility.
If false, the layer will be "faded out." Optionally, you can specify the destination
clipping coordinates of the layer by adding four more arguments (left, right, top, and
bottom):
myMotion.applyTransition(layer, fadein,
clipLeft, clipRight,
clipTop, clipBottom);
Using the Motion Object: Movement
Similar to the applyTransition(),
there is an applyMovement() function
to get a layer to move. The applyMovement()
function takes three arguments, like so:
myMotion.applyMovement(layer, x, y);
Here layer is the layer getting moved,
x is the destination x coordinate
and y is the destination y
coordinate of the layer's top-left corner.
Updating A Motion
After you've created a Motion Object, it's possible to change attributes of the Motion
Object. This way you can still apply motions in sequence, but they don't have to be
the same type of motions. To update a Motion Object's attributes, use the
changeAttribute() function.
Here's the syntax:
myMotion.changeAttribute(attributeName, value);
The first argument, attributeName,
refers to which attribute you want to change. It can be one of "direction",
"ex" or "length". (See above to learn what these attributes mean.) The second argument
is the value you want the attribute to have.
A Simple Example
In this example, a layer is faded in, moved across the screen, and then faded out. This
is done in sequence.
myMotion = new Motion("HVLRUD", true, .75);
myMotion.applyTransition(myLayer, true);
myMotion.applyMovement(myLayer, 400, 0);
myMotion.applyTransition(myLayer, false);
See the result!
(Note: In cases where user events initiate a motion, it is best to only have one
Motion Object controlling a layer at a time. One solution is to store the Motion
Object as a global variable. If it is null, create a new Motion Object. Otherwise,
use the existing object. This ensures that two [or more!] Motion Objects won't be
conflicting for control of the layer, leading to unpredictable results.)
Additional Features
In addition to applying transitions and movements to layers, often times it would
be useful to be able to evaluate a snippet of code in sequence. For instance, you
may want to evaluate some code, but only after an animation sequence has completed.
For this purpose, the JavaScript function
eval() has been built into the
Motion Object. If the Motion Object is in exclusive (sequence) mode, that
eval() will be evaluated
in the sequence it was called. To see more of what the Motion Object can do first
hand, check out this interactive demo.
The Sound Object
The final component of the JavaScript DHTML Utilites is the Sound Object. You can
use the sound Object to simplify the loading and playing of sounds - and you don't
have to understand Netscape's LiveAudio. Futhermore, since you can only
play one sound at once, the Sound Object provides you with an easy way to play
multiple sounds in order without worrying about it. (Note: because IE does not support
LiveAudio, the Sound Object does not do anything under IE.)
Here's an example of using the Sound Object:
mySound = new Sound();
mySound.addSound("cid.wav", "cid");
mySound.load();
mySound.playSound("cid");
The first line calls the constructor argument to create a new Sound Object.
Then, you associate sounds with the Sound Object using
addSound(), with the
first argument being the URL of the sound, and the second argument being
the name you want to associate with the sound. After you've added as many
sounds as you want, call load()
to load all of these sounds. Then, you can call
playSound() with the name
of the sound you want to play.
If you call
playSound() multiple
times, it will play those sounds in sequence (not cutting off whichever sound
is currently playing). Also, it will wait until the sound is loaded before
attempting to play it.
Here's an example. First, load the sounds.
Then, listen to the world's shortest question.
Note that if you click the previous link multiple times, it will play it as many
times as you clicked the link, without interrupting the sound that is already playing.
Final Note
The DHTML Utilities aren't meant to be a be-all, end-all solution
to interactivity and user functionality in DHTML Web pages.
They are meant to make it easier make your Web pages dynamic, whether
you already know JavaScript or not.
Since there's quite a bit you can do with the DHTML Utilities, I've provided
a quick reference sheet. If you're
developing DHTML with these utilities, print it out and keep it with you
for quick access. There are also some extra goodies described in the
reference sheet that haven't been mentioned here - you may find them useful.
About the Author
Michael Bostock
is a class of 2000 student at Princeton University in New Jersey. He is a
Computer Science major in the Engineering school. Over the summer, when he's not
interning with Netscape Communications Corporation, he enjoys snow cones,
water sports, and Papier-Mâché.
Check out these other Web Building sections: Designing: Creativity, graphics, writing, layout, architecture Scripting: Coding, programming, and authoring for web pages Producing: Site and project managing, commerce, advertising
| |