GEHC JavaScript Week 6 Browser Compatibility Browsers used for samples and examples: MSIE 6.0.2 Navigator 4.79 Navigator 6.2.3 Opera 6.05 Based on what is possible to do in some browsers, you can often run into browser incompatibilities that can hit you when you least expect it - usually it is because you do most of your development in one browser, and make sure everything works there, then either you or a web user tries the page with another browser and you learn that you need to make changes to overcome differences. The perfect example has nothing to do with JavaScript, and that is the marquis tag in MSIE. There is no equivalent in Navigator, and so either a Java applet or complicated JavaScript must be written to get the same effect. The blink tag is another example - this God-awful tag works in Netscape 4 and lower only, and not in any version of MSIE. With just JS, you can have trouble, too. Given this code: With this, we can change the image that is displayed in this place by doing this in MSIE: pinkimg.src = "blue.gif"; We've discussed this before - this is a recipe for disaster, but an easy trap to fall into if you use MSIE most of the time. ** JS 6-1 - gives an example of code that works fine in MSIE but breaks in NN In MSIE, img1 is a perfectly good global object - every named thing on a page becomes an object that the JS code can refer to. However, in NN, just naming something does no more than provide a way to access that object as a property of the document object. Hence, the error that Netscape generates for this code: img1 is not defined. As an aside, note that the 6-1 code works in Opera 6. There are two approaches to fixing a problem like this, and which you use may depend on a few things. If you have a bunch of JS code that is already written for MSIE, and you wish to adapt it to NN, then you can use one approach. If you're aware of the problems before you start, you can write your code so that it is compatible from the start. To make the code compatible from the start, we have to use the "document." in front of every object - both major browsers will work with this. However, if you wish to eliminate the use of the document object, you can use the adaptation method. In that method, all we need to do is ensure that if an object does not exist, that we make it exist. I'll use a function called "fixobjects". The first version of fixobjects should work, based on what is mentioned in the text (page 42), but I had trouble with it: function fixobjects() { if (!pink) pink = document.pink; if (!blue) blue = document.blue; } If we run this in the onLoad event of the body tag, then we can make sure that our code will work with either browser. In the case of MSIE, the pink and blue object have value, so nothing changes. In NN, the pink and blue objects are undefined, and we create them with the code. Note that I do *not* use the var keyword here. By not using the var keyword, JS creates these in NN with global scope. If I'd used the var keyword, then the variables would have had local scope only and would have disappeared after the fixobjects function is finished. Now, as I said, this should work ... but it did not. This method would be ideal because it seems to use core JS tests that have been around forever. But on the first line, the "if (!img1)", I got the same "not defined" message that I got before - so, only trust your tests, and never just your texts. Here is code the uses the typeof operator, mentioned in Chapter 5. It is a little more code, but works: function fixobjects() { var type; type = typeof pink; if (type == "undefined") pink = document.pink; type = typeof blue; if (type == "undefined") blue = document.blue; } Here, I also don't use the var keyword, to ensure that everything is placed in a global scope. But this time, I use the "typeof" on object to test. This returns a string which describes the object - for a non-existent object, it returns "undefined". We can test for this, and set up new objects if need be. typeof exists in JS 1.1 and higher, which means it is not universal, but that most browsers will support it. Again, if support in all browsers is crucial, then "document." should be used. ** JS 6-2 - demonstrates the fixobjects that works, but also has a copy of the one that does not - use your own browsers to test We can expand on this basic concept to create objects that help us in our code. "document.theform.city.value" is a lot to type. We can create an alias object to shorten it: var city = document.theform.city.value; Now we can use "city" any time we need to examine or use the city.value. Note that even in MSIE, there is no automatic "city" object because city is a member of a form, so there is no conflict. ** JS 6-3 - Demonstrates the above process However, this might be limiting. ** JS 6-4 - Shows possible errors Remember, "city" now refers to a string, so other methods of the document.theform.city object are not available. If you want them to be available, make the city object equal to the city widget: var city = document.theform.city; Now, to access the value, you use "city.value" but you can also do "city.focus()". ** JS 6-5 - Shows fix Browser detection ----------------- One other way of handling multiple browsers is to not even try to make your code work with both browsers - but instead to have two versions of the code, one that works for MSIE, and another that works with NN. To do this, you have to be able to tell which browser you are working with. To that end, I'll use the detect() function that is provided on the web page. To use it, use this code: This allows you to create your own object which will contain various pieces of information about the browser that is in use. var bd = new BrowserDetector(navigator.userAgent); ** detect.js navigator.userAgent contains a string that the browser creates to identify itself. The BrowserDetector function builds and returns an object that you can get a copy of with the new keyword. bd.browser will be "Unknown," "Netscape," "Lynx," "Opera," or "IE" bd.platform will vary, but likely values are "Win95," "Win98," "Unix," etc. bd.version is the full version, like 5.5 or 4.79 bd.majorver is the major version number, like 4 or 5 bd.minorver is the minor version number, like 5 or 79 ** JS 6-6 - shows an example of BrowserDetect ** Image The text outlines several strategies for handling multiple browsers: Least-common denominator - this method is like those that we've discussed where we would always use "document.", or avoid arrays, or only use JS1.0 compatible code. If you stick with code that is all 1.0 or 1.1 compatible, you will cover most browsers out there, though you will need to check your own logs to be sure. This can also be quite limiting, so only do it if the need arises. Defensive coding - write code that uses newer features, but go back and provide your own versions of features for down-version browsers. This might be a good approach if the bulk of your users use newer browsers, as you can roll out your pages as they work in the latest versions, then go back and add new code to handle old browsers as you are able. ** JS 6-7 - uses the browser detector to determine if the Array object can be used In this code, we set up functions to create an array in old-version browsers, but hope to use the code that uses the Array object. If the Array object can be used, we set "var myarray = new Array(10)" but if not, we use the makeArray function, which is probably less efficient than using the Array object, to build an array. Then we set myarray equal to the return value of the function. Once the array is built, either way, we can use the same code to change and print the contents. The last major way of dealing with incompatibilities is to ignore the problem. If you have hardly any NN2 users, or none at all, then don't bother coding for NN2. Take a look at your browser stats and decide for yourself. ** browstat.html browstat2.html browstat3.html Looking at just these lists, spread over a year, shows some real shifts. MSIE 5.5 started out strong but within a year was firmly replaced by MSIE 6. Mozilla 4 is relatively high, bu Mozilla 5 is catching up. Other older browsers are still hanging on, like MSIE 3 and Mozilla 3, even a handful of WebTV users. Opera 6 has increased as well. Test, test, test ---------------- The only real way for you to know if your stuff is working is to test it. Bring up your page in as many different versions of browsers as you can. If you create test pages on your web site, you can quickly go to them from any connected PC and check out a new browser. How did MSIE 3 render a table? Find a machine with MSIE 3, load your page, and save the screen image. If nothin else, at least you can see what it looks like. About to upgrade MSIE? Visit all your test pages and get that screen image. A test page could be very simple, just exercizing some basic HTML, or very complex. ** Display Tester ** Display Graphic Note differences in Quote, Marquis, Subscript, Superscript. Note the different line spacing in NN6. These small things can affect your page. Detecting the JavaScript version -------------------------------- If you decide to use features based not on browser type and version, but on JavaScript version, then I have seen code suggested that allows you to set a global variable based on the version that the browser knows it is. ** JS 6-8 - shows the suggested code for this technique In MSIE 5.5 and NN 4.79, this seems to work but... ** JS 6-9 - shows that this method may not be as reliable as it is purported to be. Here I added the 1.8 and 2.0 code to illustrate that I don't think this works very well. Since neither implements 1.8 nor 2.0, these values should not be set - but they are. The point? Test test test. It can be impossible to have multiple versions of MSIE, because of the way this browser works, but you can get multiple versions of NN and Opera - go to browsers.evolt.org to get installs of older browsers. Don't forget Macintosh users! The noscript tag ---------------- The noscript tags allow you to add code that is supposed to display in browsers that do not have scripting at all. This works if your browser does not know about the script tags and you place the HTML comments in the code as we normally have been doing. The problem is that these tags were introduced with Navigator 3 and MSIE 4, so the data in the noscript tags might display even if scripting is used. One final topic --------------- Suppressing multiple submits - this can be a problem if you have mouse-happy users. You may have seen messages like "please only click submit once to avoid being billed multiple times..." Here is a small bit of JS to prevent that from happening yourself: var alreadySubmitted = 0; function validateForm() { if (alreadySubmitted == 1) { alert("You have already submitted."); return false; } // validate alreadySubmitted = 1; return false; // or true, if really submitting } ** JS 6-10 - illustration See the last link for links to more browser detection scripts and to the Evolt site.