11.3. Object Detection, Encapsulation, and Cross-Browser ObjectsWith the release of CSS and Netscape's Navigator 4.x, as well as Microsoft's Internet Explorer 4.x, web-page developers could finally create sophisticated page effects such as animated page contents, collapsing menus, and in-page notifications. The only problem was that not all of the browsers used the same object model when providing this capability. One way around this cross-browser incompatibility was to access the agent string to determine what browser was accessing the page, and change the JavaScript accordingly. However, this approach, commonly called browser sniffing, was abandoned fairly quickly in favor of another approach: object detection. 11.3.1. Object DetectionWith object detection, the JavaScript accesses the object being detected in a conditional statement. If the object doesn't exist, the condition evaluates to false. In Chapter 9, I mentioned one object that's commonly used in older scripts: document.all. Checking for document.all can detect a browser that supports the IE 4.x model. Another common object detection is to check for document.layers, which was supported by Netscape's Navigator 4.x: if (document.layers) ... Luckily, all modern browsers support a fairly consistent model. All support the document.getElementById, which is critical for accessing specific elements. All support the style property (covered in the next chapter), which allows you to change the CSS style properties of an element. Still, even now, there are differences. Though I'll cover JavaScript manipulation of CSS properties in Chapter 12, we'll look at one specific property that differs between Internet Explorer and other browsers: opacity. An element's transparency is determined by the percentage of its opacity. Microsoft was the first to provide a way to change an element's opacity dynamically, through a proprietary filter called the alpha filter. Later, the Mozilla group created a variation of the filter, called the moz-opacity. At about the same time, the KHTML effort (represented by the Safari browser and Konquerer on Linux) derived a property called khtml-opacity. With the release of CSS 3.0, a universal property was defined for opacity, simply named opacity. The Mozilla line of browsers has moved to the new CSS3 standard, as has Safari. Oddly enough, Microsoft has decided not to support this property and still persists in using the alpha filter, even with the new IE 7. Object detection is necessary, then, to create an effect that works with IE as well as the other browsers that support the CSS3 opacity property. In Example 11-4, object detection is used to determine which approach to usethe alpha filter or setting the CSS opacity. The target is an image embedded in the page. Its opacity is decreased 10 percent each time the page is clicked. Because the Microsoft alpha filter uses a percentage rather than a digital value, the variable used to hold the current opacity is multiplied by 100 when used with IE. Example 11-4. Using object detection to determine how to adjust the opacity style
In Mozilla, Navigator, Camino, Firefox, Safari, and IE, the image loses opacity with each click, fading away until it's completely transparent. With Opera, which doesn't support opacity, the message is given instead.
This is an effective technique to work around cross-browser differences, but you might be asking yourself, what does this have to do with creating custom objects? 11.3.2. Encapsulating ObjectsEarlier I touched on being able to pass page objects in as a parameter when constructing a new object. The custom object then wraps, or encapsulates, the page object, allowing you to create a set of functionality that hides most of the implementation details. When using a library that has this capability, instead of having to provide all of the JS yourself to change an object's opacity, you can just call a method that changes it for you. If the underlying implementation changes because of what the browser supports, object encapsulation can hide all of the details for managing this alteration. The applications don't have to change because the underlying implementations have. This makes sophisticated interactive and dynamic applications so much easier to develop. If the browser's implementation is modified, you no longer have to worry about changing multiple applications. Additionally, you no longer have to run a continuous set of operations that check whether the browser supports this functionality. Your code, or the JS library you're using, checks it up front when the objects are created (usually when the page loads). Example 11-5 shows a self-contained application that demonstrates how object encapsulation can work in JavaScript, and how to manage cross-browser differences. The application includes a tiny object library that manages opacity. The page has two DIV elements, each of which contains an image. Both elements are positioned absolutely in the page: one is opaque, the other transparent. When the page loads, a function is called that creates an instance of the custom object, passing in each DIV element in turn. The first element's opacity is set to 1.0 (visible); the second to 0 (completely transparent). Clicking on the page decreases the opacity of the visible object and increases the opacity of the originally invisible object, creating a transformation effect between the two objects. Example 11-5. Object encapsulation
In the example, rather than implementing the methods directly in the object, they're implemented outside as separate functions. You can use this approach if you're creating cross-browser objects where all versions of the objects can use some of the methods, such as the getOpacity function (which uses object detection each time it's called), but some methods are specific to types of support (such as the two methods for changing the opacity of the object, set by object detection when the object is created). It also, in my opinion, can make the code a little easier to read as you document each function, and you don't have an excessive amount of nesting.
The use of object detection, custom objects, and encapsulation is not as important today as it was in the past when browser DHTML support varied rather significantly. However, it's still a great way to hide browser differences, not to mention enforce the old "code once, use many times" philosophy of application development. Note the DOM Level 2 functionality of getElementsByTagName to access all DIV elements, which are then passed to the custom-object constructor to be wrapped in all that cross-browser goodness. For allover page effects, wrapping the page elements in DIV elements and then encapsulating each as a custom object is an approach that simplifies the development of more sophisticated functionality. We'll look at this in more detail in the next two chapters. |