reptile7's JavaScript blog
Tuesday, December 01, 2009
The First Browser War, Revisited
Blog Entry #164

There are two browser sniffers in the series of scripts discussed by HTML Goodies' JavaScript Script Tips:

(1) Script Tips #10-12 present a bare-bones browser sniffer that uses a single navigator.appName-based test to distinguish between Netscape users and MSIE users.

(2) Script Tips #13-15 present a more elaborate browser sniffer that largely relies on navigator.appVersion returns to distinguish between users of various versions of MSIE and Netscape.

The Script Tips #10-12 and #13-15 scripts both send users of a specific browser/version to a different page with proprietary content appropriate for those users, which is an all-right thing to do if the destination page contains a lot of such content, but if you want to deploy only one or two proprietary features, is it really worth it to create separate pages for separate categories of users?

HTML Goodies' "Internal Browser Test" tutorial, our focus today, offers a simple and obvious approach to dealing with isolated proprietary features. Directly place the proprietary code in the block of an if statement that flags the browser(s) that can execute that code:

if (yourBrowser == "Browser X") {
proprietary code... }

Users browsing with Browser X will get the intended effect; the browsers of other users will skip over the if block. If desired, you can alert those other users to what they're missing via an accompanying else statement

else {
window.alert("You could be enjoying a really cool feature if you were surfing with Browser X."; }

but this is optional, of course.

Script overview

According to its "Getting Specific" section, the "Internal Browser Test" tutorial was written in May 1999. In reflection of the browsers in use at that time, Joe wrote for the tutorial a script containing a series of if statements that use navigator.appName and navigator.appVersion returns to test if the user's browser is MSIE 4+, Netscape 4+, or an earlier MSIE/Netscape version. Joe first applies his script to printing out a simple text string that identifies the user's browser in general terms, for example:

if (navigator.appName == "Netscape" && navigator.appVersion >= "4.0")
{ document.write("Running Netscape 4 or better?"); }
// Go here for the rest of the code.

The appName property did faithfully differentiate Netscape from Microsoft Internet Explorer back in the day, but is no longer a reliable tool for determining the make of a browser. I know of several non-Netscape browsers, including Firefox and Safari, that return Netscape for the appName property; similarly, Opera gives a Microsoft Internet Explorer appName return in some situations. If Browser X identifies itself with Browser Y's appName, does it follow that Browser X supports what Browser Y supports? This might be true some of the time, but I wouldn't bank on it.

The appVersion property returns a string beginning with a floating-point numeral, which may or may not match the actual version number of the browser, and containing other information. The navigator.appVersion >= "4.0" test is not a conventional comparison between two numbers* but rather a 'lexicographical' comparison between two strings based on the Unicode (hexadecimal) code points of their respective characters.

In illustration, let's suppose that I am surfing with Netscape 4.61, whose appVersion return on my computer is 4.61 (Macintosh; I; PPC). In comparing 4.61 (Macintosh; I; PPC) and 4.0 as strings, we begin with their first characters, which are both 4, so we go to their second characters, which are both ., so we go to their third characters, where we find a 6 vs. 0 difference. The Unicode code points of 6 and 0 are U+0036 and U+0030, respectively, and therefore 4.61 (Macintosh; I; PPC) is "greater than" 4.0 and the navigator.appVersion >= "4.0" test will return true.

*Alternatively, a comparison between numbers could be carried out via the top-level parseFloat( ) function:
if (navigator.appName == "Netscape" && parseFloat(navigator.appVersion) >= 4.0) { ... }

The numeral at the start of the appVersion string exactly or approximately matches the browser version number for 'level 4' and earlier browsers, but this is not the case for subsequent browsers (most of the time), as Joe himself points out for MSIE 5 in the "Getting Specific" section. Here are the appVersion returns for the (JavaScript-supporting) OS X browsers on my computer:
Camino 2.0:	5.0 (Macintosh; en)
Firefox 3.5.5:	5.0 (Macintosh; en-US)
MSIE 5.2.3:	4.0 (compatible; MSIE 5.0; Macintosh; I; PPC)
Opera 10.10:	9.80 (Macintosh; Intel Mac OS X; U; en)
Safari 4.0.4:	5.0 (Macintosh; U; Intel Mac OS X 10_5_8; en-us) AppleWebKit/531.21.8 (KHTML, like Gecko) Version/4.0.4 Safari/531.21.10
So, what else does Joe do with his MSIE/Netscape 4± tests, huh?

Span, title, cursor

Joe uses his MSIE 4+ test to write to the page an HTML Goodies link whose underlying anchor element is given a cursor:help; inline style and whose parent is a span element equipped with a title='Gotta Go To Goodies' attribute: standard code that has long had cross-browser support.

if (navigator.appName == "Microsoft Internet Explorer" && navigator.appVersion >= "4.0")
{ document.write("<span title='Gotta Go To Goodies'><a href='' style='cursor:help;'>HTML Goodies</a></span>"); }
// Go here for the rest of the code.

Rather than send you to Joe's demo page, at which the title/cursor effects will only be observable by MSIE and some Opera users, let's demonstrate the above HTML/CSS here and now, shall we? Move your mouse cursor over the HTML Goodies link below (but don't click it, unless you really do want to go to the HTML Goodies home page):

HTML Goodies

Your mouse cursor should change to or display a question mark (that's what cursor:help; does) and a Gotta Go To Goodies tooltip should pop up (that's what title='Gotta Go To Goodies' does).

Quick history:
• The W3C implemented the span element in HTML 4.
• Prior to HTML 4, the title attribute was only valid for the anchor and link elements; HTML 4 now applies it to all but a handful of elements.
• As a CSS property, cursor was introduced in CSS level 2; previously it was a proprietary Microsoft DHTML property.

Netscape 4.x can't handle the above code because, although it does support the span element,
(1) it doesn't support the title attribute for the span or any other element, and
(2) it doesn't support the cursor property.
Netscape/Mozilla has supported title as a general element attribute and the CSS cursor property from Netscape 6 onward.

Back to the layer element

Falsely reasoning that you can safely assume that all versions above 4.0 will continue to support what 4.0 does - ouch! - Joe uses his Netscape 4+ test to write to the page a layer element whose visibility is toggled by moving the mouse cursor over and away from a Go to Goodies link:

if (navigator.appName == "Netscape" && navigator.appVersion >= "4.0") {
document.write("<layer name='layer1' visibility='hide' bgcolor='#ff00ff' width='100' height='100' top='75' left='450'><center><br><br>Gotta Go To Goodies</center></layer>");
document.write("<center><a href='' onmouseover=document.layer1.visibility='show' onmouseout=document.layer1.visibility='hide'>Go to Goodies</a></center>"); }

More specifically, in a maximized window on a 1024 x 768 screen, mousing over the Go to Goodies link would cause a 100px-by-100px, fuchsia-background

Gotta Go To Goodies
block of content to appear directly below the link, and mousing out from the link would cause that block of content to disappear, if your browser were to support the layer element/object. But the layer element/object is only supported by Netscape 4.x - Netscape abandoned it for Netscape 6. For those of you with an historical bent, the layer element/object and its attributes/properties are described in Chapters 8 and 9 of Netscape's "Dynamic HTML in Netscape Communicator" resource, although the above layer code is reasonably intuitive and, more importantly, is easily retooled to a corresponding cross-browser div-based layout:

#div1 {
visibility: hidden;
background-color: fuchsia;
width: 100px; height: 100px;
position: absolute; top: 75px; left: 450px; }
#div1, #div2 { text-align: center; }

<div id="div1"><br /><br />Gotta Go To Goodies</div>
<div id="div2"><a href="" onmouseover="document.getElementById('div1').style.visibility='visible';" onmouseout="document.getElementById('div1').style.visibility='hidden';">Go To Goodies</a></div>

Move your mouse cursor over and away from the Go to Goodies link in the div below to try out Joe's layer/my div effect with your browser:

<title>Layer to Div</title>

I've switched the div order, subtracted the positioning, and functionized the commands for the above demo.

Let me set your home page

Joe wraps up the tutorial by discussing a separate script that conditionalizes proprietary code for changing a user's home page, a topic that deserves more space than a quick paragraph at the end of an entry, so we'll take it up next time.


Comments: Post a Comment

<< Home

Powered by Blogger

Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)