[ Team LiB ] Previous Section Next Section

5.6 Detecting Object Support

NN 2, IE 3

5.6.1 Problem

You want scripts to run on all browsers that support the objects that your scripts address, and to degrade gracefully in other browsers.

5.6.2 Solution

Surround the script statements that reference potentially incompatible objects with if statements that test for the existence of the objects. The objects you test for can be core JavaScript language objects, as well as DOM objects. Facilitating this kind of condition testing is the fact that a reference to a nonexistent object inside an if condition evaluates to the equivalent of false. A very common usage of object detection from earlier scriptable browsers is in scripts that work with img elements as objects for rollover image swaps (covered in depth in Chapter 12). Support for the img element object was very uneven until the Version 4 browsers. Creating browser version filters for all the possibilities would have been tedious at best. Instead, all script statements that referenced img element objects were wrapped inside an if construction that looks for the presence of the document.images array:

function rollover(imgName, imgSrc) {
    if (document.images) {
        document.images[imgName].src = imgSrc;
    }
}

This function is invoked by an onmouseover event handler associated with a link surrounding an image:

<a href="product.html" 
    onmouseover="rollover('products', 'images/products_on.gif'); return false" 
    onmouseout="rollover('products', 'images/products_off.gif'); return false">
<img src="images/products_off.gif" name="products" border="0" alt="Products Page">
</a>

The check for document.images works conveniently because if a browser recognizes an img element as a scriptable object, the document object always has an images[ ] array associated with it. If there are no images on the page, the array is empty�but it still exists with length zero. If the browser has no document.images array, however, the if condition fails, and no internal statements of the function execute. Browsers that would otherwise choke on the invalid reference to the img element object glide past the offensive statements.

5.6.3 Discussion

Object detection frees you from the tyranny of browser-version sniffing, but you must also deploy this technique in such a way that browsers not supporting your desired objects degrade gracefully. This means that you must anticipate what will happen to a script that runs in a browser that supports only some, but not all, desired objects. For example, consider the scenario in which you have a complex operation running under script control, and execution branches periodically to other functions to retrieve a calculated value. If one of those remote functions performs object detection, how well does your main execution thread respond to the inability of one remote function to return a suitable value?

The following two functions are written without the benefit of object detection. After the main function invokes a subroutine function to calculate the area of all images on the page, the main function enters the values into a form field:

function getImgAreas( ) {
    // initialize return value so we can add to it
    var result = 0;
    // loop through all img objects on the page
    for (var i = 0; i < document.images.length; i++) {
        // accumulate image areas
        result += (document.images[i].width * document.images[i].height);
    }
    return result;
}
   
function reportImageArea( ) {
    document.reportForm.imgData.value = getImgAreas( );
}

A browser that does not know about the img element object would report a script error when executing the getImgAreas( ) function. Even if the errors were hidden from view (see Recipe 4.7), the user might expect some information to appear in the text box, but none would come.

A smarter scripter would recognize that the preceding scripts might fail in very old browsers, where script errors tend to be rather invasive. To prevent such errors, the author makes two modifications. First, the potentially offending script statements in the getImgAreas( ) function wrap themselves in an object detection block. Second, the main function intelligently accommodates the possibility that the getImgAreas( ) function won't be doing any calculations at all:

function getImgAreas( ) {
    var result;
    // make sure browser supports img element objects
    if (document.images) {
        // initialize return value so we can add to it
        result = 0;
        // loop through all img objects on the page
        for (var i = 0; i < document.images.length; i++) {
            // accumulate image areas
            result += (document.images[i].width * document.images[i].height);
        }
    }
    // returned value is either a number or null
    return result;
}
function reportImageArea( ) {
    // grab result so we can verify it
    var imgArea = getImgAreas( );
    var output;
    if (imgArea =  = null) {
        // message for browsers not supporting img object
        output = "Unknown";
    } else {
        output = imgArea;
    }
    document.reportForm.imgData.value = output;
}

Notice that the main function does not perform any object detection for the form-related statement at the end. A knowledgeable scripter (or one with a good DOM reference resource) knows that the syntax used to reference the form's text field is completely backward-compatible to the very earliest browsers.

Object detection alone is not always a savior, however. Sometimes you must also employ property and method detection. For example, in the preceding subroutine function, there was no compatibility problem with accessing the height and width properties of the img element object, since these properties have been available for this object since the first implementation of that object in the various DOMs. But some other properties, such as the alt property, are not supported in all browsers that support the img object. If the subroutine function needed the alt property, further object property detection, as shown in Recipe 5.7, would be in order.

5.6.4 See Also

Recipe 5.7 to test for the presence of an object property or method; Recipe 4.6 for special values you can use in condition statements that evaluate to true and false.

    [ Team LiB ] Previous Section Next Section