Creating Cross-Browser Objects
February 1st 1998
I wrote an article in the August issue of the then NetscapeWorld
online magazine (now called Netscape Enterprise Developer)
that talked about cross-browser compatibility with Dynamic HTML and
other Web technologies (access the article in the resource section).
At that time I talked about my first implementation of cross-browser
objects, scripting objects that hide the browser implementation
details from the web developer.
The premise was simple: if both
browsers support moving an element by changing the value for the left attribute
for the element, but they use different techniques, then create a new object that contains the
browser-specific code and provide a method that results in the same behavior.
One object is then created for
Microsoft Internet Explorer, and one object for Netscape Navigator.
I expanded on the concept in a book I wrote for IDG Books on
Dynamic HTML (the "Power Guide to Dynamic HTML", published by
IDG Books, January, 1998). In the book I created scripting
objects that hid the details of Netscape's and Microsoft's
positioning implementation, and then used these objects to create some fairly complex
DHTML applications.
I expanded on these cross-browser objects for use at my Web site, adding new
properties and methods, and used these objects as the base for building higher level
complex animations and actions. The objects are created in JavaScript, and using a technique
that is ECMAScript compliant (resource section) and that both Microsoft and Netscape support.
My first step in designing the objects was to determine which methods and properties the objects
would have. One rule I follow when creating objects is always, always start simple. A
successful object is one in which all changes are positive changes. This means that methods
and properties are never removed from the object but added, only. Based on this, changes can
be made to the object without having any impact to the applications already using the objects.
The cross-browser objects I am creating for WDVL are to be used to create two different types
of "index card" like applications. These applications will show and hide content as well
as move the content around the page. Following from the "keep it simple" premise, and
the known requirements, the WDVL cross-browser objects include pointers to functions to hide
and show the element and move the element based
on setting its left or top values.
I also wanted to access the element's current left and top
value, to make an incremental movement rather than an absolute movement, and created methods for
getting these two values.
To create the objects, I first needed to create a function which acts as the constructor
for each object. This function would take a reference to an HTML element and assign this
reference to a specific object property. The function would also set pointers to functions that
act as object methods. The actual methods are created a little later in the article. The code for the Navigator object follows:
// create NS cross-browser object
function wdvlNewObject(obj) {
this.obj = obj;
// cannot assign object directly to "this"
this.objHide = objHide;
this.objShow = objShow;
this.objGetLeft = objGetLeft;
this.objGetTop = objGetTop;
this.objSetTop = objSetTop;
this.objSetLeft = objSetLeft;
}
The function is called wdvlNewObject, and it contains an object property holding the
reference to the
object as well as pointers to the six methods discussed earlier.
Why methods instead of properties?
Assigning a new value to a property does not activate a change. The change is activated
when the property of the underlying object is changed, not our derived cross-browser object.
Additionally,
I also follow the OO-based recommendation of setting and accessing
an object's properties through methods rather than through direct access to the properties,
a process referred to as data encapsulation. With this approach, changes to the
underlying objects can be made without impact on the applications using the objects.
The IE 4.x object is very similar to the Navigator object, except that a reference to the
style property/object is kept, not a reference to the object itself:
// create IE DHTML equalizer object
function wdvlNewObject(obj) {
this.obj = obj.style;
this.name = obj.id;
this.objHide = objHide;
this.objShow = objShow;
this.objGetLeft = objGetLeft;
this.objGetTop = objGetTop;
this.objSetTop = objSetTop;
this.objSetLeft = objSetLeft;
}
I could have set the object reference directly to the object, and there are advantages to
this approach. By setting the reference directly to the object, I can then access and change
properties other than just those available through the style object/attribute.
However, following from the original premise of keeping
the object simple, at this time the object reference is set to
the object's style property/object.
When these objects are used to create the second of the index card applications, you will
see how the underlying object is changed for the IE 4.x object without having to
change any application using the objects, the real strength of using object technology.
Now that the object constructor has been created, the methods for each browser need to be
coded. These are very simple methods, with one line of code, at most. The methods for the
Navigator object are:
// hide element
function objHide() {
this.obj.visibility = "hidden";
}
// show element
function objShow() {
this.obj.visibility = "inherit";
}
// element's left position
function objGetLeft() {
return this.obj.left;
}
// element's top position
function objGetTop () {
return this.obj.top;
}
// set element's top position
function objSetTop(top) {
this.obj.top = top;
}
// set element's left position
function objSetLeft(left) {
this.obj.left = left;
}
And the methods for the IE 4.x object are:
// hide element
function objHide() {
this.obj.visibility = "hidden";
}
// show element
function objShow() {
this.obj.visibility = "inherit";
}
// element's left position
function objGetLeft() {
return this.obj.pixelLeft;
}
// element's top position
function objGetTop () {
return this.obj.pixelTop;
}
// set element's top position
function objSetTop (top) {
this.obj.pixelTop = top;
}
// set element's left position
function objSetLeft(left) {
this.obj.pixelLeft = left;
}
The final step to take to create the objects is to create a routine that traverses the Web
page and pulls the objects into an array. This routine is then called during the page onLoad
event, to instantiate the cross-browser objects. The instantiation routine for
Navigator is:
// function to instantiate equalizer objects
function wdvlCreateObjects() {
wdvlObjs = new Array(document.layers.length);
for (i = 0; i < document.layers.length; i++)
// check to see if Netscape assigned block
if (document.layers[i].id.substring(0,3) != "_js")
wdvlObjs[document.layers[i].id] =
new wdvlNewObject(document.layers[i]);
}
The function uses the Navigator Layers built-in array to access all
layer elements. A layer element is one defined using the LAYER tag, or positioned using a
CSS-P style sheet. If a LAYER element is used, the element is named using the NAME
property. For DIV blocks, the element is named with the ID proprety. As I iterate through the array, I only pull in elements that have
been given a name, as the WDVL cross-browser objects are only accessed by their names.
However, Netscape assigns a default name for all objects that do not have names, a name that
begins with the prefix "_js".
So, in the instantiation code, I check the first three characters of the identifier to
see if it matches this prefix, and if not, I include the layer in my array of objects.
By only including named layers in the cross-browser object array, the developer can then
control which elements become cross-browser elements or not, based on the simple technique
of giving the element a name.
The IE 4.x setup function is shown next. Unlike Navigator, Microsoft pulls in all of the
HTML elements within the page into its collection named all. The routine
fine tunes which elements are selected by only going for the DIV block elements, and only
those that have names:
// function to create named and unnamed objects
function wdvlCreateObjects() {
theelements = document.all.tags("DIV");
wdvlObjs = new Array();
for (i = 0; i < theelements.length; i++){
if (theelements[i].id != "") {
wdvlObjs[theelements[i].id] =
new wdvlNewObject(theelements[i]);
}
}
}
Once the object code for each browser is created, it is saved in its own JavaScript source
code file, ie4_obj.js for the IE 4.x object and ns4_obj.js
for the Navigator 4.x object. Next up is how to incorporate these objects into a Web page.
|