Reconciling the differences between the object and rendering models of Navigator 4 and Internet Explorer 4 is one of the biggest challenges you face if you want to script positionable elements for both platforms in the same HTML document. The key factors to take into account are:
How to address positionable elements when the object references are so vastly different
How to make adjustments to differently named properties in a truly interactive and dynamic environment
You cannot avoid having your scripts branch to execute platform-specific statements. What you must decide for your application is how and where the branching occurs. There are three basic techniques you can use to implement cross-platform position scripting in a document:
Explicit branching
Platform-equivalent referencing
Custom APIs
Explicit branching and platform-equivalent referencing place the branching code directly in your scripts. For a limited amount of scripted positioning, having all the branching code in your scripts is manageable and actually easier to debug. But if you are doing a serious amounts of scripted positioning, a custom API lets you push the ugly branching code off to the side in an external library. In essence, you create a meta-language that gives you control over the specific syntax used in both browsers. A custom API requires a lot more work up front, but once the API code is debugged, the API simplifies not only the current scripting job, but any subsequent pages that need the same level of scriptability.
Regardless of the approach you take, you will need to set up global variable Boolean flags (JavaScript global variables scope only within the current document) that indicate which browser is running the script. In the same code that establishes those variables, you should include code that redirects browsers not capable of rendering positionable elements to another page that explains the browser requirements. Unlike pages that use regular style sheets, which generally degrade acceptably for older browsers, pages with positioned elements fare very poorly when viewed with older browsers, especially if the intended design includes overlapping and/or hidden elements.
JavaScript provides many ways to set browser flags. Some of the variation depends on how granular you want the detection to be. Browser detection can get down to the x.0x version, if a particular feature you use is buggy in earlier releases. You must also decide if detection only needs to find a floor for compatibility (e.g., Version 4 or later) or should be restricted to one generation of browser only.
Browser makers have been pretty good about maintaining backward
compatibility for browsers. Therefore, it is generally safe to let
the browser detection script set the flag when the browser version is
greater than or equal to the minimum version you need for your
application. This technique lets the page you write today run
tomorrow on the next major release of the browser. Example 4.1 shows a script sequence that should run as a
page loads, to set flags arbitrarily named isNav
and isIE
; the script also redirects older browsers
to another page.
Example 4-1. A JavaScript Browser Detection Script
var isNav, isIE if (parseInt(navigator.appVersion) >= 4) { if (navigator.appName == "Netscape") { isNav = true } else { isIE = true } } if (!isNav && !isIE) { top.location.href = "noDHTML.htm" }
With the two flags initialized as null
values in
the first statement, you can safely use either one as a control
structure condition expression, since a value of
null
evaluates to false
in
those situations. That’s precisely how the last
if
statement operates (but with the flags preceded
by the !
operator, since the script is interested
in the values not being true).
For the occasional need to control
the property of a positionable element, an explicit branch does the
job without a lot of fuss. All you need to do is determine the
platform-specific versions of the statement(s) to be executed and
embed them inside a simple
if
construction. Example 4.2 shows a script fragment whose job it is to
move an element (named face
) to a particular
coordinate point relative to the positioning context of the body. For
the Navigator version, the script takes advantage of the
layer
object’s moveTo()
method; for IE, the script adjusts the pixelLeft
and pixelTop
properties. Notice, too, that the
object references follow the conventions of the respective
browser’s object model.
Example 4-2. Simple Branching
function placeIt() { if (isNav) { document.face.moveTo(25,15) } else { document.all.face.style.pixelLeft = 25 document.all.face.style.pixelTop = 15 } }
There is no prohibition against using this technique on a complex document involving dozens of such branches. The primary penalty is the unnecessarily expanded amount of script code in the document. For some scripters, however, this technique is easiest to debug. It also comes in handy when the positionable objects are nested to different depths. Other techniques discussed in the following sections can also work with layers at different levels in Navigator, but usually not as easily.
Platform-equivalent referencing involves finding a common denominator approach to building references to positionable objects on both platforms. One way to do this is to create global variables to hold the platform-specific components of object references.
If you study the format of references to the Internet Explorer style properties of positionable objects, you see they always fall into the following format:
document.all.elementName
.style
In contrast, single-level-deep Navigator layer
objects are referenced according to the following format:
document.layerName
If you assign the unique Internet Explorer pieces to global variables
when running in that browser, but assign empty strings to those same
globals when running in Navigator, you can use the JavaScript
eval()
function to derive a valid object reference
for either browser by assembling one reference, as shown in Example 4.3. This example embeds the global variable
setting in the script segment that also sets the browser Boolean
flags. It concludes with a function that takes advantage of the
identical property names for a particular positioning property in
both browsers.
Example 4-3. Platform-Equivalent Variable Setting and Object Evaluation
var isNav, isIE var coll = "" var styleObj = "" if (parseInt(navigator.appVersion) >= 4) { if (navigator.appName == "Netscape") { isNav = true } else { isIE = true coll = "all." styleObj = ".style" } } // set stacking order of "face" element function setFaceZOrder(n) { var obj = eval("document." + coll + "face" + styleObj) obj.zIndex = n }
Notice that the variables for the IE reference
pieces—coll
(for collection) and
styleObj
(for style
object)—contain specific punctuation to assist the
eval()
function in assembling a proper string
representation of the reference for conversion to a genuine object
reference.
The platform-equivalent reference technique is particularly helpful for cases where the property names are identical on both platforms, as shown in Example 4.3. But you can also combine this technique with explicit branching to handle more complex tasks. Example 4.4 shows a hybrid approach to moving an element, adapted from Example 4.2.
If you find yourself doing a lot of scripting of positionable elements in your applications, it is probably worth the effort to create a custom API that you can link into any application you create. A custom API can take care of the “grunt” work for common position-scripting tasks, such as moving, hiding, showing, and resizing elements, as well as setting background colors or patterns. When you define a custom API library, the methods you write become the interface between your application’s scripts and various positioning tasks.
Example 4.5 gives you a sample of what such an API library might look like. The API defines the following functions:
-
getObject(
obj
)
Takes a positionable element from the default positioning context and returns an object reference for either the Navigator
layer
or the Internet Explorerstyle
object-
shiftTo(
obj, x, y
)
Moves an object to a coordinate point within its positioning context
-
shiftBy(
obj, deltaX, deltaY
)
Moves an object by the specified number of pixels in the X and Y axes of the object’s positioning context
-
setZIndex(
obj, zOrder
)
Sets the z-index value of the object
-
setBGColor(
obj, color
)
Sets the background color of the object
-
show(
obj
)
Makes the object visible
-
hide(
obj
)
Makes the object invisible
-
getObjectLeft(
obj
)
Returns the left pixel coordinate of the object within its positioning context
-
getObjectTop(
obj
)
Returns the top pixel coordinate of the object within its positioning context
Example 4-5. A Custom API for Positionable Elements
// DHTMLapi.js custom API for cross-platform // object positioning by Danny Goodman (http://www.dannyg.com) // Global variables var isNav, isIE var coll = "" var styleObj = "" if (parseInt(navigator.appVersion) >= 4) { if (navigator.appName == "Netscape") { isNav = true } else { isIE = true coll = "all." styleObj = ".style" } } // Convert object name string or object reference // into a valid object reference function getObject(obj) { var theObj if (typeof obj == "string") { theObj = eval("document." + coll + obj + styleObj) } else { theObj = obj } return theObj } // Positioning an object at a specific pixel coordinate function shiftTo(obj, x, y) { var theObj = getObject(obj) if (isNav) { theObj.moveTo(x,y) } else { theObj.pixelLeft = x theObj.pixelTop = y } } // Moving an object by x and/or y pixels function shiftBy(obj, deltaX, deltaY) { var theObj = getObject(obj) if (isNav) { theObj.moveBy(deltaX, deltaY) } else { theObj.pixelLeft += deltaX theObj.pixelTop += deltaY } } // Setting the z-order of an object function setZIndex(obj, zOrder) { var theObj = getObject(obj) theObj.zIndex = zOrder } // Setting the background color of an object function setBGColor(obj, color) { var theObj = getObject(obj) if (isNav) { theObj.bgColor = color } else { theObj.backgroundColor = color } } // Setting the visibility of an object to visible function show(obj) { var theObj = getObject(obj) theObj.visibility = "visible" } // Setting the visibility of an object to hidden function hide(obj) { var theObj = getObject(obj) theObj.visibility = "hidden" } // Retrieving the x coordinate of a positionable object function getObjectLeft(obj) { var theObj = getObject(obj) if (isNav) { return theObj.left } else { return theObj.pixelLeft } } // Retrieving the y coordinate of a positionable object function getObjectTop(obj) { var theObj = getObject(obj) if (isNav) { return theObj.top } else { return theObj.pixelTop } }
Notice that every function call in the API invokes the
getObject()
function. If the parameter passed to a
function is already an object, the object reference is passed through
to the function’s other statements. Thus, you might use a
combination of techniques to work with nested objects, as in the
following call to a custom API function:
if (isNav) { setBGColor(document.outer.document.inner, "red") } else { setBGColor(document.all.inner.style, "red") }
The custom API in Example 4.5 is provided as a starting point for you to create your own extensions that fit the kinds of positioning tasks your applications require. Your version will probably grow over time, as you further enhance the positioning techniques used in your applications.
When you write a custom API, save the code in a file with any
filename that uses the .js extension. Then, you
can link the library into an HTML document with the following tag
pair in the HEAD
portion of the document:
<SCRIPT LANGUAGE="JavaScript" SRC="myAPI.js"></SCRIPT>
Once you do this, all the functions and global variables in the custom API library become immediately available to all script statements in the HTML document.
Get Dynamic HTML: The Definitive Reference now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.