Team LiB
Previous Section Next Section

What to Detect

When performing browser detection, it is important to be aware of the different aspects that affect how your site will be displayed. You can roughly break up the useful detectable information into four categories:

We can use JavaScript to obtain information about each one of these categories. Only the “delivery” category presents significant challenges. We’ll address it briefly later in the chapter. First, let’s take a look at what technical facilities can be detected via JavaScript.

Technology Detection

When it comes to browser technology, you would usually like to know the browser’s support for the following things:

  • Markup

  • Style sheets

  • Scripting languages

  • Java

  • Object technology (plug-ins and ActiveX controls)

Markup and style sheets are a bit difficult to detect. You might try to use the DOM to check basic markup support by probing to create a particular object or using the document.implementation.hasFeature( ) method. This method returns a Boolean value if a particular HTML or XML level of binding is supported, for example:

var HTMLDOM1 = document.implementation.hasFeature('HTML', '1.0');
// contains true or false indicating HTML binding support

Of course, few browsers support DOM techniques well enough to really rely on them, and even if they did, such probes really say nothing about the actual support of a particular markup or style facility. In short, just because you can instantiate an (X)HTML element and set some attributes using the DOM, it doesn’t mean those attributes actually do anything in the browser! For now, you will have to rely on your knowledge of browser support for particular versions of HTML or CSS. Fortunately, the other items on our technology list can more easily be addressed from JavaScript.

JavaScript Detection

JavaScript support is probably the easiest technology to detect; if a script doesn’t run, this condition implicitly shows that the browser doesn’t support JavaScript or that it is turned off. Consider the use of the <<noscript>> tag here with a <<meta>> redirection:

<<!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>>JS Check<</title>>
<<noscript>>
<<meta http-equiv="Refresh" CONTENT="0; URL=noscript.html" />>
<</noscript>>
<<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" />>
<</head>>
<<body>>
<<script type="text/javascript">>
<<!--
 document.write("This page has JavaScript!");
// -->>
<</script>>
<</body>>
<</html>>
Note 

The previous example will not validate in the w3c validator, because of the omission of the <<noscript>> tag within the <<head>> of a document. The authors look at this as an omission from the (X)HTML specifications, given the fact that <<script>> can be placed in the <<head>>, and suggest this usage despite the lack of validation.

If they have disabled scripting or have accessed the site with a very old browser, the user is redirected to a “noscript.html” page in your site’s errors directory like the one here.

<<!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>>Error: No JavaScript Support<</title>>
<<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" />>
<</head>>
<<body>>
<<h1>>Error: JavaScript Support Required<</h1>>
<<hr/>>
<<p>>Your browser does not appear to support JavaScript or it is turned off.<<br/>>
Please enable JavaScript or upgrade your browser and then return to the page 
in question.<</p>>
<<p>>If you believe you reached this page in error please contact 
<<a href="mailto:webmaster@democompany.com">>Webmaster<</a>><</p>>
<</body>>
<</html>>

An even better error page could read the referring entry in the request using a server-side scripting environment and record information about which page the user came from, and then provide a link back to that page once the user has corrected the error.

Some developers opt instead to do a positive check: the use of JavaScript redirecting the user to a particular page using the Location object. For example:

<<script type="text/javascript">>
<<!--
 window.location="scripton.html";
//-->>
<</script>>

The problem with this approach is that it tends to be used as a single detection point and disrupts the Back button facility in the browser. The first technique is a more passive approach and can be easily included on all pages without serious worry.

JavaScript Version Detection

While it is easy to detect if JavaScript is on or off, what about version or feature support? One way to deal with different versions of JavaScript is to utilize the non-standard language attribute of the <<script>> tag. While in most of this book we have used the standard type attribute to indicate the scripting language in use, the language attribute is actually commonly used and has some extra value. Recall from Chapter 1 that JavaScript-aware browsers will ignore the contents of <<script>> tags with language attributes they do not support. Because browsers act in this way, it is possible to create multiple versions of a script for various versions of the language or to set a variable to indicate the highest version supported, as in this example:

<<script language="JavaScript">>
// JS 1.0 features
 var version="1.0";
<</script>>

<<script language ="JavaScript1.1">>
// JS 1.1 features 
var version="1.1";
<</script>>

<<script language="JavaScript1.2">>
// JS 1.2 features
var version="1.2";
<</script>>

<<script language="JavaScript1.5">>
// JS 1.5 features
var version="1.5";
<</script>>

We could even declare dummy functions or objects and then redefine them in higher versions to avoid errors using this fall-through method. This technique is illustrated in Chapter 23, yet fall-through code isn’t always the best way to deal with multiple versions of JavaScript.

Note 

One problem with the fall-through technique is that the language and type attributes when used together are not respected consistently in browsers. In some cases type overrides language, and in other browsers it is the other way around. If you use this technique stick to just the language attribute.

JavaScript Object Detection

In some cases we don’t care about whether a particular version of JavaScript is being used but whether certain objects or methods are available. For example, consider how we dealt with image rollovers and various DHTML ideas in Chapter 15 using object detection. We found that, rather than knowing everything about which browsers support what versions of JavaScript, it is probably better just to detect for capabilities by checking whether the appropriate object is available. For example, the script here checks to see if your browser could support rollover images by determining whether the image[] collection is defined:

<<script type="text/javascript">>
if (document.images)
   alert("Rollovers would probably work");
else
   alert("Sorry no rollovers");
<</script>>

Here we relied on the fact that JavaScript’s dynamic type conversion will convert a non-existent object to false, and if it exists it will evaluate as true.

As the previous example showed, object detection is a simple way to figure out if a feature is supported or not. However, be careful with relying on object detection too much. Far too often in JavaScript, we assume that the existence of one object implies the existence of other objects or the use of a particular browser, but this is not always the case. For example, we might use code like

var ie = (document.all) ? true : false;

to detect if Internet Explorer is in use. However, does the existence of document.all really mean that Internet Explorer is in use? The truth of the matter is that another browser could support document.all but not necessarily provide all the features found in Internet Explorer. The developer might even be simulating document.all with their own code. Given all the possibilities for trouble, it might be better to check for each object specifically, so instead we might use

var allObject = (document.all) ? true : false;
var getById = (document.getElementById) ? true : false;

and so on. In some ways, object detection is the best method to use, but it should be used carefully and assumptions shouldn’t be made.

Another consideration with object detection is not to go too far too quickly. Remember that probing a property of a nonexistent object throws an error, so first check to see if the object exists. As an example, if you were checking for window.screen.height and you just did

if (window.screen.height)
  // do something

you would throw an error in browsers that did not support the Screen object. Instead you could rely on short-circuit evaluation to do the test incrementally, like so:

if (window.screen && window.screen.height)
        // do something

Advanced JavaScript programmers might see that the object detection approach fits nicely with try/catch blocks.

Java Detection

Detecting Java’s availability is fairly easy using the Navigator method javaEnabled(). This method returns true if Java is available and turned on, and false otherwise.

if (navigator.javaEnabled())
   // do Java stuff or write out <<applet>> tag
else
   alert("Sorry no Java");

You can find out more about Java once you know it is available by accessing a Java applet included in the page. You can even determine what type of Java Virtual Machine is supported. In order to do this, you will have to access the public methods and properties of a Java applet. Interacting with applets is discussed in more detail in Chapter 18.

Plug-in Detection

In Netscape 3+ (and Opera 4+), each plug-in installed in the browser has an entry in the plugins[] array of the Navigator object. Each entry in this array is a Plugin object containing information about the specific vendor and version of the component. A simple detection scheme checks for a plug-in’s existence using the associative array aspect of JavaScript collections. For example, to look for a Flash plug-in, you might write

if (navigator.plugins["Shockwave Flash"])
   alert("You have Flash!");
else
   alert("Sorry no Flash");

Of course, you need to be careful to use the exact name of the particular plug-in in which you are interested. It is important to note that different versions of the same plug-in can have different names, so you need to carefully check vendor documentation when detecting plug-ins in this fashion. Also be aware that Internet Explorer defines a faux plugins[] array as a property of Navigator. It does so in order to prevent poorly written Netscape-specific scripts from throwing errors while they probe for plug-ins or simply returning the wrong result. We would need to deal with this cross-browser nuance by checking to make sure we are not using Internet Explorer when doing the plugins[] array probe, as shown here:

if (navigator.appName.indexOf('Microsoft')==-1 ||        
   (navigator.plugins && navigator.plugins.length))  
 {
   if (navigator.plugins["Shockwave Flash"])
      alert("You have Flash!");
   else
      alert("Sorry no Flash");
 }
else
   alert("Undetectable: Rely on <<object>> tag");

Fortunately, if Internet Explorer is in use we can rely on the <<object>> tag to install the appropriate object handler if the user allows it. More information about detecting and interacting with objects such as Netscape plug-ins and Microsoft ActiveX controls can be found in Chapter 18.

Visual Detection: Screen Object

The Screen object is available in 4.x (and later) browsers and indicates the basic screen characteristics for the browser. It is actually a child of the Window object, although it would seem to make more sense as a parent of Window if you think about things logically. The following example shows the common screen characteristics that can be detected in browsers that support the Screen object.

<<!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>>Common Screen Properties<</title>>
<<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" />>
<</head>>
<<body>>
<<h2>>Current Screen Properties<</h2>>
<<script type="text/javascript">>
<<!--
if (window.screen)
{
  document.write("Height: "+screen.height+"<<br />>");
  document.write("Width:"+screen.width+"<<br />>");
  document.write("Available Height: "+screen.availHeight+"<<br />>");
  document.write("Available Width: "+screen.availWidth+"<<br />>");
  document.write("Color Depth: "+screen.colorDepth+"bit<<br />>");
}
else
 document.write("No Screen object support");
// -->>
<</script>>
<</body>>
<</html>>

A rendering of the example is shown next.

Click To expand

One thing that is rather troublesome with this detection is that the availHeight and availWidth properties indicate the height and width of the screen minus any operating system chrome rather than, as one might expect, the actual size of the available browser window. In order to detect actual window size, you have to use properties of the Window object in the case of Netscape. In the case of Internet Explorer, you need to look into the Document object and examine the body itself. However, in the case of the DOM, you might want to look at the size of the root element, namely, the <<html>> tag, and not the <<body>> if you are trying to get the dimensions of the window. Of course, which tag to look at depends on what rendering mode your browser is in, either loose or strict, which is generally determined by the doctype statement in the document. This example shows how you might check all this. Invariably, something might change given the lack of agreement among browser vendors on how to implement certain CSS, XHTML, and JavaScript ideas, but the example should still demonstrate the concept:

<<!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>>Available Region Checker<</title>>
<<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" />>
<</head>>
<<body>>
<<h2 align="center">>Resize your browser window<</h2>>
<<hr />>
<<form action="#" method="get" name="form1" id="form1">>
  Available Height: <<input type="text" name="availHeight" size="4" />><<br />>
  Available Width: <<input type="text" name="availWidth" size="4" />><<br />>
<</form>>
<<script type="text/javascript">>
<<!-- 
    var winWidth = 0;
    var winHeight = 0;

function findDimensions()
 {
    if (window.innerWidth)
       winWidth = window.innerWidth;
    else if ((document.body) && (document.body.clientWidth))
       winWidth = document.body.clientWidth;
                  
    if (window.innerHeight)
       winHeight = window.innerHeight;
    else if ((document.body) && (document.body.clientHeight))
       winHeight = document.body.clientHeight;
      /* nasty hack to deal with doctype switch in IE */
   if (document.documentElement  && document.documentElement.clientHeight && 
document.documentElement.clientWidth)
     {
      winHeight = document.documentElement.clientHeight;
      winWidth = document.documentElement.clientWidth;
      }
    document.form1.availHeight.value= winHeight;
    document.form1.availWidth.value= winWidth;
}
 findDimensions();
 window.onresize=findDimensions;
//-->>
<</script>>
<</body>>
<</html>>

A rendering of the example is shown here:

Click To expand

In browsers that permit manipulation of page content and styles at runtime, we can set the size of screen objects such as fonts in a manner appropriate to the current window size. Consider the following example, which works in Internet Explorer 5 and Netscape 6 or later.

<<!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>>Dynamic Sizing<</title>>
<<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" />>
<</head>>
<<body>>

<<h1 id="test1" style="font-family: verdana; text-align: center;">>Text grows 
and shrinks!<</h1>>

<<script type="text/javascript">>
<<!-- 
function setSize()
{
 if (document.getElementById)
  {
   theHeading = document.getElementById("test1");
   if (window.innerWidth)
      theHeading.style.fontSize = (window.innerWidth / 13)+"px";
   else if ((document.body) && (document.body.clientWidth))
      theHeading.style.fontSize = (document.body.clientWidth / 13)+"px";
  }
}
window.onload = setSize;        // call to set initial size;
window.onresize = setSize;
// -->>
<</script>>
<</body>>
<</html>>

A typical rendering is shown here, but readers are encouraged to try this example themselves to verify its usefulness.

Click To expand

Under browsers like Internet Explorer that support expressions within CSS rules, we might use something cleaner like this:

<<h1 style="font-family: verdana; text-align: center;
     font-size: expression(document.body.clientWidth / 13)">>
Internet Explorer Font Sizing!<</h1>>
Note 

It might be even better to avoid using JavaScript to size objects in CSS and instead rely on relative sizing measurements, like percentage or em values.

Besides sizing, we might also dynamically address color issues on the Web using JavaScript. For example, many designers still use reduced color images that stick to a limited 216-color palette, called the “browser-safe” palette, when they might be able to use richer images in many situations. The following code could be used to insert different types of images conditionally:

<<script type="text/javascript">>
<<!-- 
 if (window.screen)
  {                                // Sense the bit depth...
   if (screen.colorDepth >> 8)
      document.writeln('<<img src="nonsafecolors.gif" />>');
   else
      document.writeln('<<img src="safecolors.gif" />>');
  }
else
   document.writeln('<<img src="safecolors.gif" />>');
// -->>
<</script>>
<<!-- Deal with the script off or non-JS aware browsers -->>
<<noscript>>
  <<img src="safecolors.gif" />>
<</noscript>>

Language Detection

The final form of basic detection is to use JavaScript to sense which language the user’s browser is set to support. We might use this to send users to a Spanish page if they have the Spanish language set as a preference in their browser. Browsers provide access to this information in slightly different ways. Netscape and Opera use the window.navigator.language property, while Internet Explorer relies on window.navigator.userLanguage or window.navigator.systemLanguage. In the case of Internet Explorer, there is some lack of clarity regarding whether we should pay attention to the operating system language or the browser language. A good guess would be to focus on the browser’s language—and it’s a good idea to provide links on pages to select other languages in case the detection is incorrect. The following simple example illustrates the use of these properties and could easily be extended using the Location object to redirect users to language-specific pages after sensing.

var lang = "en-us";
if (window.navigator.language)
  lang = window.navigator.language
else if (window.navigator.userLanguage)
  lang = window.navigator.userLanguage
if (lang == "es")
  document.write("Hola amigo!");
else
  document.write("Hi friend!"); 
Note 

There is some concern about the accuracy of the language information available in JavaScript, and some developers suggest looking at the user-agent string to see if anything is specified there as well.


Team LiB
Previous Section Next Section