Scripting for 5th Generation Browsers Part 1
Introduction
The release of NS6 has been a hotly debated topic on the net. At the center of this debate have been claims of lack of functionality and usability. To an extent these claims are accurate as the browser does have some usability issues, e.g. non-functional rollovers, as one example. However, approaching Netscape 6 from this negative perspective and focusing on the things that do not work is a limiting viewpoint. It is limiting because ‘we’ as web developers tend to loose sight of the bigger picture and consequently do not focus on what the browser can do in terms of coding practices. This bigger picture entails a web that allows developers to code by a singular standard. Herein lies the beauty of Netscape 6, a standards compliant browser, a factor that often seems to get lost in the usability debate. Consequently, for the first time on the WWW, developers can mostly focus on coding by a singular standard rather than having to work their way through a number of conditional JavaScript statements to cater to different browser requirements. I say mostly, because there are still some specific situations where DOM switches catering for Internet Explorer 5+ and Netscape 6 need to be provided, e.g., detecting a browsers height and width. In the main though a web developer’s focal point can be centered upon coding by a singular standard, which represents a considerable improvement in coding techniques.
While I understand that catering to Netscape 4 for commercial reasons is still essential for many web developers, I think it is equally important to understand that the future (emphasis on future) of the web does not lay in providing backward compatible solutions catering to proprietary technology. Rather the future of web development seems inherently tied to coding by a singular standard as evidenced by browsers beginning to adopt more W3C recommendations than at any other time in the history of the WWW. Consequently, it is imperative to begin to understand how to code for the leading browsers of today.
Which brings us to the purpose of this article. In this article I would like to expose the reader to some coding methods that cater for Internet Explorer 5+ and Netscape 6+. Hopefully, the coding examples provided here will serve the function of wetting the appetites of web developers and make them more eager to embrace the dawn of a new era.
Code Reduction
One of the biggest benefits of coding for IE5+ and Netscape 6 is the code reduction that is obtained by not having to include conditional branches (e.g. If else statements). For example, let us suppose we wanted to dynamically change the background color of a layer in Netscape 4, Internet Explorer 4 and Netscape 6. We would have to create conditional statements to cater for the different Document Object Models as demonstrated in the below example:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"><html>
<head>
<title>Change The Background Color</title>
<script>
function changelayerColor() {
if (document.all)
document.all.Layer1.style.backgroundColor = 'red';
else if (document.getElementById)
document.getElementById('Layer1').style.backgroundColor = 'red';
else if (document.layers)
document.Layer1.bgColor = 'red';
}
</script>
</head>
<body bgcolor="#FFFFFF" text="#000000">
<a href="#" onMouseover="changelayerColor()">Change Layer Background Color</a>
<div id="Layer1" style="position:absolute; width:200px; height:115px; z-index:1;
left: 203px; top: 84px; background-color: #0066FF;
layer-background-color: #0066FF; border: 1px none #000000">
</div>
</body>
</html>
However, if you wanted to code by W3C standards the above script would translate to such:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"><html>
<head>
<title>W3C Change Layer Background Color</title>
<script>
function changeColor() {
document.getElementById('Layer1').style.backgroundColor = 'red';
}
</script>
</head>
<body bgcolor="#FFFFFF" text="#000000">
<a href="#" onMouseover="changeColor()">Change Layer Background Color</a>
<div id="Layer1" style="position:absolute; width:200px; height:115px; z-index:1;
left: 203px; top: 84px; background-color: #0066FF">
</div>
</body>
</html>
As is demonstrated in the above example, there is a considerable reduction in the code. The above script functions in Internet Explorer 5+ and Netscape 6 as these both support the W3C DOM document.getElementById() method. At last a standard way of scripting that is compatible in both browsers! From my perspective, that is the real benefit of coding in such a manner.
What Is A Standard Anyway?
I have already mentioned coding by W3C standards, but what makes more sense to me is that the standard that matters most is what is currently supported by both browsers without having to rely on proprietary methods and consequently conditional branches. That is not to say I am not an advocate of W3C standards, because the reality is I believe in this fundamental concept and support it as much as possible.
However, I am constantly drawn back to the practical as opposed to strictly coding by W3C methods, particularly when both browsers have the same implementation for a method. For example innerHTML which is not a W3C recommendation is supported by both Netscape 6 and Internet Explorer, so to me it makes sense to take advantage of this powerful technique. For example:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Write Content to Layer</title>
<script>
<!--
function writetoLyr(name, message) {
document.getElementById(name).innerHTML = message;
}
//-->
</script>
</head>
<body>
<a href="javascript:writetoLyr('contentLayer', '<p>Eddie Was Here </p>')">
Write Something to the Layer
</a>
<div id="contentLayer" STYLE="position: absolute; left: 400px; top: 300px;
width: 370px; height: 0px; z-index: 6; visibility: visible border: none ">
</div>
</body>
</html>
For all intents and purposes the standard that matters most is what is common to both browsers. At least that is how I typically approach things; to find the common denominator for both browsers. In many ways, the future looks a lot brighter as more DOM related methods get implemented into browsers. For example, Internet Explorer 6 now supports the replaceData() method which is handy for manipulating text strings. Try this example in Internet Explorer 6: Extracting DOM Substrings
In fact if you work through the DOM tips examples at Javascript Tip archive you will find that quite a few things that were not working in Internet Explorer 5 and 5.5 now work with Internet Explorer 6. The point being that common standards between browsers are frequently being updated and it’s a developers “duty” to keep a keen eye on the changes that are occurring. Thus it is not just the W3C we need to keep an eye for new recommendations, but also what the new browsers have in common that fall outside of the W3C recommendations realm. Both are equally important.
For the moment though we need to focus on the here and now, so let us begin by taking a look at how to retrieve elements.
Retrieving Elements
The two most common methods of retrieving elements based on the W3C DOM are the document.getElementById() and document.getElementsByTagName() methods. Both of these methods are important to learn as they form the basis of much of what follows. The document.getElementById() method as the name implies retrieves an elements id by passing the id as an argument. For example;
<div id="eddieLayer" style="position:absolute; width:200px; height:115px; z-index:1; left: 40px; top: 80px; visibility: visible"> This is a CSS Layer with an id of eddieLayer </div>
would be retrieved with JavaScript by using
theDiv = document.getElementById('eddieLayer').style
We can then utilize the variable theDiv to dynamically manipulate the style attributes of the <div> tag as indicated below:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Manipulating a Div Element</title>
<script language="JavaScript" type="text/javascript">
function moveDiv() {
theDiv = document.getElementById('eddieLayer').style;
theDiv.left = 400;
}
</script>
</head>
<body bgcolor="#FFFFFF">
<a href="#" onMouseDown="moveDiv()"> Lets move the div </a>
<div id="eddieLayer" style="position:absolute; width:200px;
height:115px; z-index:1; left: 40px; top: 80px; visibility: visible">
This is a CSS Layer with an id of eddieLayer
</div>
</body>
</html>
We can also refine the moveDiv() function to a single line of script as such:
function moveDiv() {
document.getElementById('eddieLayer').style.left=400;
}
However, it is often more useful to define a variable for later use, because that variable can be used repeatedly. For example,
theDiv.left = 400; theDiv.top = 500; theDiv.visibility = hidden; theDiv.width = 350;
By using the document.getElementById() method and adding the style attributes as in document.getElementById('eddieLayer').style every CSS attribute of that layer becomes exposed to dynamic manipulation. In short, it provides web developers with a range of options when working with Dynamic HTML web pages which goes beyond just manipulating a CSS layer.
For instance, let us suppose we wanted to directly manipulate an image element that is not in an CSS layer, we could use exactly the same methods as above, but with the provision that we assign a unique id to the image and add the style="position:absolute; attribute as the next example shows.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Manipulating a Image</title>
<script language="JavaScript" type="text/javascript">
function moveImage() {
theImg = document.getElementById('dolphin').style;
theImg.left = 400;
}
</script>
</head>
<body bgcolor="#FFFFFF">
<p><a href="#" onMouseDown="moveImage()">Lets move the image</a></p>
<p><img src="dolphin.jpg" id="dolphin"
style="position:absolute;width="480" height="360"></p>
</body>
</html>
I can almost see the light switching on in peoples’ heads as they begin to realize the inherent potential of this method. The restriction of the document.getElementById() method is that it used to access individual elements that have a unique id attribute.
A way to overcome this limitation is the document.getElementsByTagName() collection. This method takes the tag type as its argument and returns all of the tags of that particular type in an html document. In essence it is a collection method as in returning an array. For example,
theImg= document.getElementsByTagName("img");
returns all the images in a document regardless of whether they have a name or id attribute. The following example returns an alert that displays the number of images in the document as a way of demonstrating how elements are collected by using the document.getElementsByTagName() method.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Retrieving How Many Images In a Document</title>
<script language="JavaScript" type="text/javascript">
function getImages(){
theImg= document.getElementsByTagName("img");
alert(theImg.length)
}
</script>
</head>
<body bgcolor="#FFFFFF">
<p><a href="#" onMouseDown="getImages()">How many images in this document?</a></p>
<p><img src="dolphin.jpg" width="480" height="360"> </p>
<p><img src="dolphin.jpg" width="480" height="360"> </p>
<p><img src="dolphin.jpg" width="480" height="360"> </p>
</body>
</html>
We can also traverse the DOM tree and target specific page elements. For example, let’s assume that we wanted to obtain the height of the first CSS layer in a document, then something along these lines could be employed:
getHeight = document.getElementsByTagName('div')[0].offsetHeight;
The [0] in the above line of script is an array index. In JavaScript the 0 value represents the first element in an array. Consequently, the above line of script would target the first CSS layer in the document and return its height value. The distinction to make here is that the document.getElementById() method retrieves elements which have a unique id, where as the document.getElementsByTagName() method could be used to either target individual elements that do not have a unique id assigned to them or a retrieve a collection of the same tag.
Please note that the offsetHeight and offsetWidth properties are supported by IE4+ and NS6 even though they are not a W3C recommendation.
Rollover Fun with setAttribute()
In IE5+ and NS6 there is new way to swap images for rollover effects that is W3C DOM based. The golden rule to follow is out with the name attribute and in with the id attribute. For those of you that are not aware the current W3C recommendation is to use the id attribute, even though browsers still currently will perform rollovers by using the name attribute as we have become accustomed to. But please note, that in future it is unlikely that the name attribute will be supported, hence it is important to understand the concepts presented here.
In the following example we will return to the document.getElementById() method and also introduce the concept of setting attributes via the setAttribute() method. Let’s begin by take a look at the script:
<script language="JavaScript" type="text/javascript">
img1src = new Image();
img1src.src="button2.jpg";
img2src = new Image();
img2src.src="button1.jpg";
function swapImage(){
theImg=document.getElementById("test2img");
theImg.setAttribute("src","button2.jpg");
}
function restoreImage(){
theImg=document.getElementById("test2img");
theImg.setAttribute("src","button1.jpg");
}
</script>
The first part of the script is a routine preload method so that the images that are going to be used for rollovers are loaded into the cache. Luckily, both Internet Explorer and Nescape6 pull the images from the cache by default, so this saves us hooking up the preload variables into the script. Apart from that there isn’t a lot here that we aren’t used to.
It is when we begin to look at the swapImage() function that differences from past methods occur. First we create, a variable theImg which then gets assigned to W3C document.getElementById() method. In this particular instance, we want to collect the id value of test2img by referencing it with the following syntax document.getElementById("test2img").
theImg=document.getElementById("test2img");
Now that the image is collected and referenced we can start having some fun with it by using the W3C setAttribute() command. setAttribute() allows us to define any attribute of an element, for example height or width. In this example we are going to set the src attribute.
theImg.setAttribute("src","button2.jpg");
setAttribute() allows us to define the src of the image and consequently set the new src value of the image. In this particular example, button1.jpg will be replaced with button2.jpg with a mouseOver event. To swap the image back to its original state we use the restoreImage() function. Our event handlers would then look like this:
<a href="#" onMouseOver="swapImage()" onMouseOut="restoreImage()"> <img src="button1.jpg" width="38" height="25" border="0" id="test2img"> </a>
Note the use of id=”test2img” in the image tag. This is how the document.getElementById() method identifies that this is where to apply the script to. But wait it gets better! The same effect can be accomplished with a single line of code by using two arguments.
function swapImage(id, imgsrc) {
document.getElementById(id).setAttribute('src', imgsrc);
}
Our arguments can be used to identify what the id of the image is and what its new src value will be. To understand this a bit better take a close look at what is occurring at the event handlers in the image tag.
<a href="#" onMouseOver="swapImage('img1', 'on.jpg')"
onMouseOut="swapImage('img1', 'off.jpg')">
<img id="img1" src="off.jpg" width="59" height="59" border="0">
</a>
Let’s use the onMouseOver event handler to better comprehend what is occurring.
onMouseOver="swapImage('img1', 'on.jpg')"
We identify the id attribute with img1 by using the argument id in our swapImage() function. Next, the new src value of on.jpg is identified by utilizing the imgsrc argument The onMouseOut event handler uses the same logic but just swaps the image to off.jpg. It doesn’t get much sweeter than this folks! Or does it ?
Four State DOM Rollovers with a Single Image
Remember, that earlier I stated that the setAttribute() method allows for the setting of any attribute such as width and height? Well lets use these attributes to create a four state rollover with just a few lines of code and a single image. First the image tag:
<img src="button_eddie.png" id="Image1" border="0" width="25" height="25">
As noted previously, what is important in the above tag is to set a unique id for the image so that we can later retrieve the image element via the document.getElementById() method. The rollover script we employ to put this into action is as follows:
<script language="JavaScript" type="text/javascript">
function rollOvers(id, new_width, new_height) {
document.getElementById(id).setAttribute('width', new_width);
document.getElementById(id).setAttribute('height', new_height);
}
</script>
In this example, we utilize three arguments, id, new_width and new_height. The id argument is used to retrieve the unique id of the image, in this instance image1, and the new_width and new_height arguments will be used from the event handler to set new image dimensions.
<a href="#" onClick=="rollOvers('Image1','28','28')"
onMouseOver="rollOvers('Image1','26','26')"
onMouseOut="rollOvers('Image1','24','24') ">
Let’s consider what is occurring here for a moment. We have made four image dimensions from a single image. Effectively, we have produced a four state disjointed rollover. The four states are:
1. The initial dimensions of 25*25.
2. The onMouseOver dimensions of 26*26.
3. The onMouseOut dimensions of 24*24.
4. The onClick dimensions of 28*28.
Netscape Rollover Fix
Before you all start screaming at me with complaints that rollovers don’t work properly in Netscape 6 because of a refresh bug, let me make the point that the thing with web development is to find solutions to these types of problems wherever possible. Sometimes no solution exists and as web developers we have to wear that, but on many occasions solutions do exist, provided we start to think out of the box. Luckily, such a solution exists for the Netscape 6 refresh bug and rollovers.
To fix the rollover problem in Netscape 6 two things need to occur.
Use a timer to force the rollovers, e.g.
function forceIt(){
setTimeout("swapImage()",1);
}
and to fix the refresh bug use onUnload="history.go(0)" in the body tag.
Note that this bug only applies to Netscape 6. In Netscape 6.01 and the newer builds of Mozilla this is no longer an issue.
That’s it for this time. In part two of this tutorial we will look at creating elements on the fly, object constructors and a lot more!