Previous: Dynamic Positioning and Layers Next: Dynamic Styles and Classes

Cross-Browser DHTML Example

Okay, the $64,000 question is, "Can I possibly create one page that will work in three different browsers—IE, Netscape 4, and Netscape 6?" The answer is "Yes," because they all have some things in common that may be a little surprising. In this section, we'll look at the layer-swapping example that was used in Listings 9.9 and 9.11 to see how we can bring them all together in one page.

First, let's start with Netscape 4. The surprising bit is that you don't even have to use the <layer> element if you don't want to! Instead, Netscape 4 will accept the <div> elements and allow you to create the layers using the ID identifiers in a <style> section. That means the cross-browser DHTML example can include the following for all three browsers:

<style type="text/css">
#mydiv1 {
position:absolute;
top:50px;
left:50px;
height:400px;
width:500px;
visibility:visible;
}

#mydiv2 {
position:absolute;
top:50px;
left:50px;
height:400px;
width:500px;
visibility:hidden;
}
</style>

Likewise, all three browsers support the same basic <body> container shown back in Listing 9. They all support the <div> element and the onClick event handler.

What needs to be different for each browser is the function that changes the layers. They need to be different for two reasons:

NOTE

Technically, IE 5.5 and higher can use the Netscape 6 approach, but that would require detecting four different browser types instead of three. So, because IE 5.5 is also backward compatible with the IE 4 approach, we can use it for all IE versions.

The only way to use different functions for the different browsers is to detect the type of browser that the user has, and then work a little object magic. Here's what the testing part of the function will look like:

function changeLayer () {

  var isNet4;
  var isNet6;
  var isIE;
  if (navigator.appName == "Netscape") {
   if (navigator.appVersion.charAt(0) == "4") isNet4 = true;
   if (navigator.appVersion.charAt(0) == "5") isNet6 = true;
  }
  if (navigator.appName.indexOf("Microsoft") != -1) isIE = true;

This does a series of simple tests to determine which browsers we're working with. Here's how it works:

  1. The function begins by defining three variables.

  2. Then it tests to see what the name of the browser application is.

  3. If "Netscape" is the application's name, we'll test for the application number. Depending on the number, either the variable isNet4 or the variable isNet6 will be assigned the value true.

  4. If "Microsoft" is the application's name, the variable isIE is assigned true. (Technically, the comparison is using the indexOf method to find out if the string "Microsoft" is part of the application's name. If the indexOf value does not equal 1, that means the word "Microsoft" is in the name. It's a little convoluted, but it works.)

  5. Now, we can set up the function so that it creates variables that will fit into the final formula, depending on the browser we're using. In this section of the function, we'll set the variable visTest to hold the string that corresponds to the visibility property value we're going to test against. (When you test visibility in Netscape 4, it responds with the values hide and show instead of hidden and visible. So, you have to test especially for those values. Oddly, though, it can accept the latter two values as assignments, so we don't have to do anything different for Netscape 4 in the section that actually swaps the layers.)

  6. Then, two object variables (div1object and div2object) are created to circumvent the different ways that each browser accesses the visibility property of these layers:

  var VisTest;
  var div1Object;
  var div2Object;
  
  if (isNet4) {
   visTest = "hide";
   div1Object = document.mydiv1;
   div2Object = document.mydiv2;
   }
   
  if (isNet6) {
   visTest = "hidden";
   div1Object = document.getElementById("mydiv1").style;
   div2Object = document.getElementById("mydiv2").style;
   }
   
  if (isIE) {
   visTest = "hidden";
   div1Object = document.all.mydiv1.style;
   div2Object = document.all.mydiv2.style;
   }

Finally, the function ends with the same pattern you saw earlier in Listings 9 and 11. The visibility property is tested to see if the first layer is hidden. If it isn't, the first layer is hidden and the second layer is shown. If the first layer is hidden, the opposite happens:

  if (div1Object.visibility != visTest) {
    div1Object.visibility = "hidden";
    div2Object.visibility = "visible";
    }   
  else {
    div1Object.visibility = "visible";
    div2Object.visibility = "hidden";
    }
    
} 

As you can see, the visTest and two object variables are cleverly plugged in there to make up for the differences in the browser's approaches to the DOM. Listing 12 shows the whole page.

Listing 12 Cross-Browser Compatible Layer Switching

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Netscape Layers</title>

<style type="text/css">
#mydiv1 {
position:absolute;
top:50px;
left:50px;
height:400px;
width:500px;
visibility:visible;
}

#mydiv2 {
position:absolute;
top:50px;
left:50px;
height:400px;
width:500px;
visibility:hidden;
}
</style>

<script>
<!--

function changeLayer () {

  var isNet4; 
  var isNet6;
  var isIE;
  if (navigator.appName == "Netscape") {
   if (navigator.appVersion.charAt(0) == "4") isNet4 = true;
   if (navigator.appVersion.charAt(0) == "5") isNet6 = true;
  }
  if (navigator.appName.indexOf("Microsoft") != -1) isIE = true;

  var VisTest;
  var div1Object;
  var div2Object;
  
  if (isNet4) {
   visTest = "hide";
   div1Object = document.mydiv1;
   div2Object = document.mydiv2;
   }
   
  if (isNet6) {
   visTest = "hidden";
   div1Object = document.getElementById("mydiv1").style;
   div2Object = document.getElementById("mydiv2").style;
   }
   
  if (isIE) {
   visTest = "hidden";
   div1Object = document.all.mydiv1.style;
   div2Object = document.all.mydiv2.style;
   }
  
  
  if (div1Object.visibility != visTest) {
    div1Object.visibility = "hidden";
    div2Object.visibility = "visible";
    }   
  else {
    div1Object.visibility = "visible";
    div2Object.visibility = "hidden";
    }
    
} 

// -->
</script>

</head>

<body>

<form>
<input type="button" value="Click me" OnClick="changeLayer()">
</form>

<div id="mydiv1">

<img src="1980main.jpg" />

<p>This beautiful 3/2 farmhouse is right in the heart of Old Towne, taking you out of the 
suburbs and back to within walking distance of not only the drug store, but the drug store's 
soda fountain! Put life back the way it's supposed to be, at $125,000.</p>


</div>

<div id="mydiv2">
  
<img src="1730maple.jpg" />

<p>
Not only does this neighborhood have good schools and good shade trees, it's got great 
sidewalks. Get out and meet your new neighbors, or strap on that helmet and get back on 
that bicycle you haven't ridden in years. The bike path in front of this property takes you 
right along Creekside Drive and into the county park! This 2/2 cottage has two porches and 
a two-car garage with studio apartment. Only $135,000.
</p>

</div>

</body>
</html>

Cross-Browser APIs

In the previous section, you saw a long script that produced a reasonably simple result. You can imagine what happens when you try to create pages to do much more complicated things that work in many different browsers. Although it's perfectly common and acceptable to do all this prep work (detecting browsers and substituting pointer variables for certain DOM objects), a more sophisticated approach is to use a special application programming interface, or API.

Although APIs are usually the domain of more sophisticated programming languages, a number of cross-browser APIs for JavaScript have been developed. They give you predetermined function calls that you can use with your scripts to make things like browser detection much easier. Instead of writing the function yourself, you simply learn the API and then plug that function into your script. You'll load the API as an external script file, making its functions available to you within your own script.

If that sounds interesting, visit one of the following Web sites to learn more:

Previous: Dynamic Positioning and Layers Next: Dynamic Styles and Classes