In IE4, elements are accessed through a method similar to document.getElementById(), document.all[]; xbStyle merely takes the ID passed to getElementById(), and redirects it to document.all[] instead, which then returns the appropriate element. In Netscape 4, a <div> with an ID can be accessed only through the document.layers[] array, which is a numbered list of all layers on the page; to compensate, xbStyle checks every element in that list against the ID you're looking for, stops when it finds a match, and returns the correct element.
To your script, it doesn't matter whether xbStyle had to pass the ID to a Netscape or an IE-specific function; it sees only the element the function returned. In other words, all you have to worry about is input and output; if you input the ID, you'll get back the element. This is the essence of an API, to hide the hard stuff and let you get real work done.
Below is the source of xbStyle's getElementById() method; take a look behind the curtain and see how the magic works:
if (navigator.family == 'ie4' && navigator.version < 5) {
document.getElementById = new Function("id", "return document.all[id];");
}
else if (navigator.family == 'nn4') {
document.getElementById = nav4GetLayerById;
}
function nav4GetLayerById(id) {
return nav4FindLayer(this, id);
}
function nav4FindLayer(doc, id) {
var i;
var subdoc;
var obj;
for (i = 0; i < doc.layers.length; ++i) {
if (doc.layers[i].id && id == doc.layers[i].id)
return doc.layers[i];
subdoc = doc.layers[i].document;
obj = nav4FindLayer(subdoc, id);
if (obj != null) {
return obj;
}
return null;
}
Screenshot of the "sliding menu" in action
Cross-Browser Sliding Menus
The two functions in our cross-browser sliding menu, showLayer() and hideLayer(), mimic the behavior of our original script, while changing only what is necessary to achieve compatibility. First and foremost, the primary function of xbStyle is to create a cross-browser style object. However, rather than being an object built into all other elements, as it is in the W3C DOM, you must create a new instance of xbStyle for every element whose styles you need to manipulate. You do this by using the xbStyle constructor function, which takes one parameter, an element ID.
var styleObject = new xbStyle("id");
Once you've created a style object, you can use one of xbStyle's built-in methods, which expand on the W3C and 4.0 DOMs, to manipulate style properties. In this example, we'll be using the following methods from xbStyle:
moveBy(x, y): Moves an element a specified number of pixels along the X (horizontal) and Y (vertical) axes, relative to its current position. This method is equivalent to adding to or subtracting from both the "top" and "left" style properties at once.
moveTo(x, y): Moves an element to a fixed point on the page, defined by its X and Y coordinates. This method is equivalent to setting the "top" and "left" style properties at once.
getLeft(): Returns the value of the style object's "left" property as a number. Note that this is different from what the W3C DOM would return because the number includes the unit "px," for "pixels," at the end of it's value. This means the value is a text string, not a plain number. This is important to remember, because while you can compare two numbers as greater than or less than, as in the example below, you can't do the same with text.
setLeft(n): Takes one parameter, a number, and sets the "left" property of the style object.
document.getElementById(id): Yes, it's also part of the DOM, but xbStyle creates a method of the same name for 4.0 browsers that don't support the W3C DOM, with the same general functionality. If the browser does support the DOM, xbStyle does nothing, and the method works just as expected.
The documentation at DevEdge contains a full listing of all xbStyle objects, methods, and properties.
So, let's take a look at the new and improved sliding menu script, now using the methods above (the letters on the left are for reference only):
function showLayer() {
A var layer = document.getElementById("Layer1");
B var styleObj = new xbStyle(layer);
C var left = styleObj.getLeft();
var timeout = setTimeout("showLayer()", 1);
D if (left <= 0) {
E styleObj.moveBy(5, 0);
}
else {
styleObj.moveTo(0, 50);
F clearTimeout(timeout);
}
}
function hideLayer() {
var layer = document.getElementById("Layer1");
var styleObj = new xbStyle(layer);
G styleObj.setLeft("-75");
}
A. Here is the first usage of document.getElementById(), now possible in all 4.0 and 5.0 browsers.
B. We create a new xbStyle object, which provides an interface to dynamic styles across browsers. Because the various Netscape and IE browsers have wildly varying DHTML style support, we must create a new xbStyle object, and associate it with an HTML element every time to ensure that a style object exists, and that it works consistently.
C. We store the menu's "left" style property in a variable named left, so that two lines later we can compare its current position with its final position.
D. Here we check if some part of the menu is still off the screen by comparing the current left position we've stored as "left" with its final position, 0 pixels from the left edge of the screen. If the menu is still partly offscreen, the function moves the menu, and repeats.
E. In the previous version of this script, we moved the menu along 5 pixels by adding 5 to the current left position, and setting the "left" property to that sum. Now, with xbStyle, we can just use the moveBy() method, and save ourselves some math.
F. Similarly, we replace the code to return the menu to its original position with this method, moveTo().
G. Finally, we use setLeft() to return the menu to its original position.
Try adapting some of your scripts to use XBStyle as well, and you'll save yourself a lot of coding and debugging time while reaching a larger Web audience.
Bill Pena
is a freelance Web/information designer and writer. He was also the designer for Safari: Tech Books Online, O'Reilly's online books service.
What views do you have about the techniques Bill Pena has outlined in this article? You must be logged in to the O'Reilly Network to post a talkback.
Showing messages 1 through 2 of 2.
Flatten Layers Tree Instead of Searching It
2002-02-01 22:54:03
mszlazak
[Reply | View]
How about "flattening" the "layers tree" by referencing its layers with an associative array.
Then it would be like the document.all[] or document.getElementById() arrays and repeated tree reversals wouldn't be needed. After all, the worst case senerio for getting one layers reference is to traverse the entire tree. Multiple layers with the same id would be required an added dimension or property or just not allowed.
You introduce an excellent means of dealing with the source of most web developer's gray hairs and headaches. If you don't already know about it, check out http://sourceforge.net/projects/dynapi to see a fully developed client-side JavaScript API that's been evolving for a number of years now.
Then it would be like the document.all[] or document.getElementById() arrays and repeated tree reversals wouldn't be needed. After all, the worst case senerio for getting one layers reference is to traverse the entire tree. Multiple layers with the same id would be required an added dimension or property or just not allowed.