18. Compatibility Techniques
Contents:
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 we JavaScript programmers must bear in mind. The one fact that we must always remember is that it is a heterogeneous net out there. Your JavaScript programs will be run on many different platforms, using browsers from possibly many different vendors, and for any given browser, using various versions of the browser. This can be difficult to remember for those of us who come from the non-portable past when programs were developed on a platform-specific basis. Remember: it doesn't matter what platform we develop a program on. It may work fine on that platform, but the real test is whether it works fine (or fails gracefully) on all platforms. The compatibility issues to be aware of fall into two broad categories: platform, browser, and version-specific features or bugs, and language-level incompatibilities, including the incompatibility of JavaScript with non-JavaScript browsers. 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! 18.1 Platform, Browser, and Version- Specific CompatibilityWhen developing production-quality JavaScript code, testing and knowledge of platform-specific incompatibilities are your chief allies. If you know, for example, that Navigator 2.0 on Macintosh platforms always gets the time wrong by about an hour, then you can take steps to deal with this. If you know that Windows platforms do not automatically clear your setting of the status line when the mouse moves off of a hypertext link, then you can provide an appropriate event handler to explicitly clear the status line. If you know that Internet Explorer 3.0 uses ActiveX to communicate with java applet while Navigator uses Netscape's LiveConnect mechanism, you can write a page that uses the appropriate mechanism depending on the browser currently in use. Knowledge of existing incompatibilities is crucial to writing compatible code, and you'll probably find Appendix B, Known Bugs, Appendix C, Differences between Navigator 2.0 and 3.0, and Appendix D, JavaScript Incompatibilities in Internet Explorer 3.0, quite helpful in this area. Once you have identified an area of incompatibility, there are a number of basic approaches you can take to coping with it. They are described in the following subsections. The Least-Common-Denominator ApproachOne technique for dealing with incompatibilities is to avoid them like the plague. For example, the Date object is notoriously buggy in Navigator 2.0. If you want Navigator 2.0 users to be able to use your programs, then you can simply avoid relying upon the Date object altogether. As another example, Navigator 3.0 and Internet Explorer 3.0 both support the opener property of the Window object, but Navigator 2.0 does not. The least-common-denominator approach says that you should not use this property. Instead, you can create an equivalent property of your own whenever you open an new window:
newwin = window.open("", "new", "width=500, height=300"); newwin.creator = self; With this technique you use only features that are known to work everywhere. It doesn't allow you to write cutting-edge programs or push the envelope, but it results in very portable, safe programs that can serve a lot of important functions. Defensive CodingWith the "defensive coding" approach to compatibility you write code that contains platform-independent workarounds for platform-specific incompatibilities. For example, if you set the status property of a Window object from the onMouseOver() event handler to display a custom message in the status line, the status line will be cleared when you move the mouse off the hyperlink on all platforms except the crucial Windows platform. To correct for this, you might just get into the habit of including an onMouseOut() event handler to clear the status line. To return to the example of the opener property from above, the defensive coding approach to compatibility does not discard the property altogether, but does insert a workaround to take care of platforms that do not support the property:
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 above. The same technique works to test for the existence of methods. For example, the split() method of the String object only exists for JavaScript 1.1 implementations, so using defensive coding we would write our own version of this function that works for JavaScript 1.0 and JavaScript 1.1. But for efficiency we'd like to use the fast built-in method on those platforms that do support it. Our platform-independent code to split() a string might end up looking like this:
if (s.split) // if method exists, use it a = s.split(":"); else // otherwise, use our alternative implementation a = mysplit(s, ":"); Defensive coding using platform-independent workarounds is a useful and practical approach to incompatibilities. It relies on being able to come up with appropriate platform-independent workarounds, such as the following ingenious workaround for the Navigator 2.0 Macintosh date-skew bug, invented by Bill Dortch:
function FixDate(d) { // Create a new Date(0) to detect any skew, and subtract it. d.setTime(d.getTime - (new Date(0)).getTime()) } Platform-Specific WorkaroundsWhen the least-common denominator and defensive coding approaches to incompatibilities won't work, you may find yourself having to create platform-specific workarounds. Recall from Chapter 13, The Navigator, Location, and History Objects, that the navigator property of the Window object provides information about the vendor and version of the browser and about the platform it is running on. You can use this information to insert code that is very platform-specific into your program. You might use this approach to distinguish between Navigator and Internet Explorer, for example, when working with Java applets or data embedded with the <EMBED> tag. Another example of a platform-specific workaround might involve the bgColor property of the Document object. On Windows and Mac platforms, you can set this property at run time to change the background color of a document. Unfortunately, when you do this on Unix platforms, 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 Navigator object to test for Unix platforms and simply skip the special effect for those platforms.[1] The code could look like this:
if (navigator.appVersion.substring("X11") == -1) // if not a Unix platform fade_bg_color(); // then do the special effect
Ignore the ProblemAn important question to ask when considering any incompatibility is "how important is it?" If the incompatibility is a minor or cosmetic one, or affects a browser or platform that is not widely used, or only affects an out-of-date version of a browser, then you might simply decide to ignore the problem and let the users affected by it cope with it on their own. For example, earlier we suggested defining an onMouseOut() event handler to correct for the fact that Navigator 2.0 and 3.0 for Windows do not correctly clear the status line. Unfortunately, the onMouseOut() event handler does not exist in Navigator 2.0, so this workaround won't work for that platform. If you expect your application to have a lot of users who use Navigator 2.0 on Windows, and you think that it is really important to get that status line cleared, then you'll have to develop some other workaround. For example, you could use setTimeout() in your onMouseOver() event handler to arrange for the status line to be cleared in two seconds. But this solution brings problems with it--what if the mouse is still over the hypertext link and the status line shouldn't be cleared in two seconds--and a simpler approach in this case might really be to ignore the problem. Fail GracefullyFinally, there are some incompatibilities that cannot be ignored and that cannot be worked around. In this case, your programs should work correctly on all platforms, browsers, and versions that provide the needed features, and should fail gracefully on all others. Failing gracefully means recognizing that the required features are not available and informing the user that they will not be able to use your JavaScript program. For example, the image replacement technique we saw in Chapter 16, Special Effects with Images, does not work in Navigator 2.0 or Internet Explorer 3.0, and there is really no workaround that can simulate it. Therefore, we should not even attempt to run the program on those platforms--instead we should politely notify the user of the incompatibility. Failing gracefully can be harder than it sounds. Much of the rest of this chapter explains techniques for doing so. |
|