home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


JavaScript: The Definitive GuideJavaScript: The Definitive GuideSearch this book

Chapter 20. Compatibility Techniques

JavaScript, like Java, is one of a new breed of platform-independent languages. That is, you can develop a program in JavaScript and expect to run it unchanged in a JavaScript-enabled web browser running on any type of computer with any type of operating system. Though this is the ideal, we live in an imperfect world and have not yet reached that state of perfection.

There are, and probably always will be, compatibility problems that JavaScript programmers must bear in mind. The one fact that we must always remember is that it is a heterogeneous network out there. Your JavaScript programs may run on three or more operating systems, using three or more versions of browsers from at least two different vendors. This can be difficult to keep in mind for those of us who come from the nonportable past, when programs were developed on a platform-specific basis. Remember: which platform you develop a program on doesn't matter. It may work fine on that platform, but the real test is whether it works (or fails gracefully) on all platforms on which it is used.

The compatibility issues fall into two broad categories: platform-specific, browser-specific, and version-specific features on one hand; and bugs and language-level incompatibilities, including the incompatibility of JavaScript with non-JavaScript browsers, on the other. This chapter discusses techniques for coping with compatibility issues in both of these areas. If you've worked your way through all the previous chapters in this book, you are probably an expert JavaScript programmer, and you may already be writing serious JavaScript programs. Don't release those programs on the Internet (or onto a heterogeneous intranet) before you've read this chapter, though!

20.1. Platform and Browser Compatibility

When developing production-quality JavaScript code, testing and knowledge of platform-specific, vendor-specific, and version-specific incompatibilities are your chief allies. If you know, for example, that Netscape 2 on Macintosh platforms always gets the time wrong by about an hour, you can take steps to deal with this problem. If you know that Netscape 2 and 3 on Windows platforms do not automatically clear your setting of the status line when the mouse moves off a hypertext link, you can provide an appropriate event handler to explicitly clear the status line. If you know that Internet Explorer 4 and Netscape 4 support vastly different Dynamic HTML models, you can write pages that use the appropriate mechanism depending on the browser in use.

Knowledge of existing incompatibilities is crucial to writing compatible code. Unfortunately, producing a definitive listing of all known vendor, version, and platform incompatibilities would be an enormous task. It is beyond the scope and mission of this book, and it has apparently never even been seriously attempted. You may find some assistance on the Internet, but you will have to rely primarily on your own experience and testing. Once you have identified an area of incompatibility, however, there are a number of basic approaches you can take to coping with it, as described in the following sections.

20.1.3. Feature Testing

Feature testing is a powerful technique for coping with incompatibilities. If you want to use a feature that may not be supported by all browsers, include code in your script that tests to see whether that feature is supported. If the desired feature is not supported on the current platform, either do not use it on that platform or provide alternative code that works on all platforms.

Consider again the opener property. In the least-common-denominator approach, we simply avoided the use of this property and used an alternative on all platforms. With the feature-testing approach, we provide the alternative only when the current platform does not support opener:

newwin = window.open("", "new", "width=500, height=300");
if (!newwin.opener) newwin.opener = self;

Note how we tested for the existence of the opener property. The same technique works to test for the existence of methods. For example, the split( ) method of the String object exists only for JavaScript 1.1 implementations. We can write our own version of this function that works in all versions of JavaScript, but for efficiency we'd like to use the fast, built-in method on those platforms that do support it. Thus, our feature-testing code to split( ) a string might end up looking like this:

if (s.split)              // Check if the method exists, without invoking it
    a = s.split(":");     // If it does exist, it is safe to invoke it
else                      // Otherwise:
    a = mysplit(s, ":");  // use our alternative implementation

Feature testing is commonly used for performing DHTML effects that are supported only on some browsers or are implemented differently in different browsers. For example, if you are designing a site that includes image rollover effects, you can use feature testing with code like this:

if (document.images) {  // If the browser defines an images[] array,
                        // we include image rollover code here
}
// Otherwise, we simply omit the image rollover effect

As another example, suppose we want to work with a dynamically positioned document element. Different browsers have different APIs for doing this, so we first use feature testing to see which API is supported by the current browser with code like this:

if (document.getElementById) {  // If the W3C DOM API is supported,
    // do our DHTML using the W3C DOM API
}
else if (document.all) {        // If the IE 4 API is supported,
    // do our DHTML using the IE 4 API
}
else if (document.layers) {     // If the Netscape 4 API is supported,
    // do the DHTML effect (as best we can) using the Netscape 4 API
}
else {                          // Otherwise, DHTML is not supported,
    // so provide a static alternative to DHTML, if we can
}

The nice thing about the feature-testing technique is that it results in code that is not tied to a specific list of browser vendors or browser version numbers. It works with the set of browsers that exist today and should continue to work with future browsers, whatever feature sets they implement.

20.1.4. Platform-Specific Workarounds

Feature testing is well suited to checking for support of large functional areas. You can use it to determine whether a browser supports image rollovers or the W3C DOM API, for example. On the other hand, sometimes you may need to work around individual bugs or quirks in a particular browser, and there may be no easy way to test for the existence of the bug. In this case, you will need to create a platform-specific workaround that is tied to a particular browser vendor, version, or operating system (or some combination of the three).

Recall from Chapter 13 that the navigator property of the Window object provides information about the vendor and version of the browser and the operating system on which it is running. You can use this information to insert platform-specific code into your program.

An example of a platform-specific workaround involves the bgColor property of the Document object. On Windows and Macintosh platforms, you can set this property at runtime to change the background color of a document. Unfortunately, when you do this on Unix versions of Netscape 2 and 3, the color changes but the document contents temporarily disappear. If you wanted to create a special effect using a changing background color, you could use the Netscape object to test for Unix platforms and simply skip the special effect for those platforms. The code could look like this:

// Check whether we're running Netscape 2 or 3 on a Unix platform
var nobg = (parseInt(navigator.appVersion) < 4) &&           // Version
           (navigator.appName.indexOf("Netscape") != -1) &&  // Vendor
           (navigator.appVersion.indexOf("X11") != -1);      // OS

// If we're not, then go ahead and animate the page background color
if (!nobg) animate_bg_color( );

When writing platform-specific workarounds, it is common to use " client-sniffer" code to determine what the current platform is, based (typically) on the properties of the navigator object. You run your client-sniffer code once, and it sets variables that describe the current platform. Then you don't have to reparse the properties of navigator for each platform-specific bit of code you write; you can simply use the variables set by the sniffer code. A simple sniffer that may be sufficient for many purposes might look like this:

var browserVersion = parseInt(navigator.appVersion);
var isNetscape = navigator.appName.indexOf("Netscape") != -1;
var isIE = navigator.appName.indexOf("Microsoft") != -1;
var agent = navigator.userAgent.toLowerCase( );
var isWindows = agent.indexOf("win") != -1;
var isMac = agent.indexOf("mac") != -1;
var isUnix = agent.indexOf("X11") != -1; 

With variables like these defined, you might write code like the following:

if (isNetscape && browserVersion < 4 && isUnix) {
    // Work around a bug in Netscape 3 on Unix platforms here
} 

A variety of prewritten client sniffers are available on the Internet. You can find a thorough one (along with a helpful discussion of its use) at http://www.mozilla.org/docs/web-developer/sniffer/browser_type.html.



Library Navigation Links

Copyright © 2003 O'Reilly & Associates. All rights reserved.