Section II.4. Cross-Platform Strategies


II.4. Cross-Platform Strategies

The more browser brands, versions, and operating systems you wish to support with your DHTML applications, the greater your challenge to write one code base that works with them all. Before undertaking any project intended for more than a single browser, you must make difficult decisions about not only which browsers to support but also how users of other browsers will be treated by your site. Consumer-oriented e-commerce sites, for example, can rarely afford to turn away even a small percentage of potential customers because the visitors' browsers don't measure up to a lofty design. Specialized or personal sites that are not as concerned about competitive pressures may choose to require browsers of a certain minimum functionality to pass beyond the home page, but requiring only one type of ultra-modern browser or operating system will not win you many friends.

II.4.1. Adding Value with DHTML

An important question to ask yourself about your design goals is whether the DHTML features of your site add value to the content that is otherwise accessible to all, or are those DHTML features essential to the site design. For example, a DHTML-assisted hierarchical menu system adds value by speeding direct access to a nested area of your site, yet users of DHTML-challenged browsers can still reach those areas (albeit with more clicks and intermediate stops en route), and search engine web crawlers will pursue the links. Conversely, if navigation absolutely requires DHTML powers, some visitors will be locked outnot a good thing, and perhaps even illegal if government regulations require that your site be accessible to all visitors. Similarly, search engine web crawlers, which don't execute scripts, will not know to follow links that are rendered only by script. This could reduce the chances that deeper pages of your site will be catalogued and indexed.

As noted in Online Section 1, the standards-based emphasis of HTML markup since HTML 4 has been to separate a document's structure from its presentation details. Thus, CSS is largely deployed by way of style rules either imported from external .css files or specified in the document's head. Even though the style attribute exists for most HTML elements, specifying style rules within an element's tag is fraught with maintenance peril and associates presentation too closely with a specific element's structural purpose. On the scripting side, getting the scripting out of markup (e.g., assigning event handlers through means other than, say, onclick or onchange event handler attributes inside tags) offers key benefits:

  • It encourages design of a basic, unscripted page (and essential server support for tasks such as form validation) that conveys vital information for all visitors.

  • Initialization routines assign event handlers based on scripted thresholds and criteria under your control (e.g., through object detection).

  • After the page loads, scripts can modify the document's elements to add DHTML features (e.g., inserting enhanced links that navigate to areas requiring a scriptable browser).

  • It encourages design of reusable, generic script functions and objects that rely on event object data to convey references to affected elements, instead of designing functions hard-wired to specific elements.

  • It leads to ease of maintenance or upgradability without modifying document markup.

Don't forget that a sizable portion of your audience may be using browsers designed for those with vision and motor skill impairments. And don't forget visitors using cell phones containing modest web browsers. While some of these browsers may support aspects of JavaScript, your DHTML goodies may be of no use to those visitors. This is yet another reason to treat DHTML as an added value proposition to a basic design that works without scripting or DHTML. You may have to build more of your application on the server to provide basic functionality for all visitors, while the client-side DHTML additions (which bypass some of the server stuff) give DHTML-equipped browser users a faster or more interactive experience.

The technique of separating a document's scripted behavior from its structure goes by names such as "unobtrusive" (meaning that DHTML doesn't get in the way of basic content delivery) or "progressive" (meaning that scripting features are added to a page incrementally if the browser supports them). Whatever the label, it is rooted in the notion of using DHTML scripting judiciously to make a solid page even more inviting and engaging for visitors equipped with modern DHTML-capable browsers.

II.4.2. Serving Pages by Browser Type

It's not uncommon for server programs to assemble parts of HTML pages based on information it receives from the HTTP_USERAGENT string during the client request. For example, if your site's designer has carefully crafted external style sheet files tailored to Macintosh and Windows idiosyncracies, then the server can assemble the OS-specific .css file URL for the page's <link> tag as the page is being served. The browser performs no decision making at all.

Microsoft Conditional Comments

To let page authors include HTML code (including <link> and <script> tags) for multiple versions of Internet Explorer, yet have sections activate only for specific IE versions, Microsoft devised a system called conditional comments. First supported in IE 5, the system provides a small language whose statements exist only inside HTML comment tags (and sometimes a variation of comment tags). Each statement defines a condition under which the content inside the comment tag is interpreted by the browser. Typical conditions are whether the current IE version is less than, greater than, or equal to a specified number.

As an example, if you wish to have one set of scripts work for all versions of Internet Explorer earlier than IE 7 (including versions 3 and 4) and another set for all browsers beginning with IE 7, you can isolate those sections by way of the following conditional comment sections:

 <![if lt IE 7]> <script type="text/javascript" language="JavaScript">     // statements for pre-IE7 here </script> <![endif]> <!--[if gte IE 7]> <script type="text/javascript">     // statements for IE7 and later here </script> <![endif]--> 

Markup inside the first comment (without the expected double hyphens) is interpreted by IE 3 through IE 6, as desired here, whereas the markup in the second comment is interpreted only by IE with major version numbers of 7 or greater. Browsers other than IE/Windows interpret contents of the first comment type, but not the second. Therefore, you could use conditional comments to include markup that is to be interpreted only by a specific IE version (starting with 5) but no other browsers. You can find more details in the <!--comment--> element at the end od Chapter 1 in Dynamic HTML: The Definitive Reference, Third Edition.


II.4.3. Object Detection over Browser Sniffing

Regardless of the approach you use to accommodate multiple browsers, it will at some point entail code branching or other equalization tactics that are dependent upon the scriptable features of the browser. Back when the matrix of browser versions was small, it was common practice to use browser "sniffing" with the aid of information gleaned from the navigator.userAgent and related properties information about the browser brand and version. But the matrix of installed browser brands and versions has expanded beyond a sensible number of variations. Except in rare circumstances, browser sniffing has become virtually extinct. In its place is a more viable technique known as object detection.

In case you're skeptical about the shortcomings of browser sniffing, it will be helpful to observe how DHTML developers from the Version 4 browser days found themselves in trouble at some point as new browser versions came on the scene. Consider the following typical global variable declaration from the era of IE 4 and Navigator 4:

 // code unknowingly doomed to failure var isNav, isIE; if (parseInt(navigator.appVersion) >= 4) {      isNav = (navigator.appName == "Netscape");      isIE = (navigator.appName.indexOf("Microsoft") != -1); } 

Hereafter, various functions would branch their code based on the Boolean values of isNav and isIE, with browser-specific code in each branch. Unforeseen at the time, however, subsequent versions of Netscape abandoned the layer objectusually the primary need for branching in the first place back in the Version 4 days. As a result, Netscape 6 (whose appVersion reported 5, and is thus greater than or equal to 4) attempted to execute code that it could not handle. On the IE variable side of things, two potential problems loomed, depending on how much IE4-ness the author ascribed to browsers following that branch. For one, the Macintosh version of IE did not implement most IE/Windows-only features. Second, the default preference settings of the Opera browser caused it to identify itself as IE, yet this did not assure compatibility with IE scripts.

Trying to compensate for all browsers past, present, and future requires a huge version sniffing library plus a crystal ball about future browser version numbering and naming. Even attempting such forecasting won't take into account new browsers that crop up, some of which will be built upon very capable existing engines, such as the Mozilla's Gecko engine. Building branched code based on browser version is a losing battle. A superior approach is to branch based on the capabilities of the browser. In other words, your code doesn't care what browser is running; all it cares is that the objects, properties, and methods needed for the next batch of code are supported in the current browser, whatever it may be. Object detection is based on a browser's current capabilities, not hard-wired branding or version numbering.

Object detection is a shortcut name for a technique that verifies the existence of an object, property, or method before using it in a script. The technique isn't new. Scripts that control image rollovers have been using it for years by testing for the presence of the document.images array before acting on an image object:

 if (document.images) {      // act on image objects here } 

All object models that implement img elements as objects support the document.images array. In older browsers, the expression document.images evaluates to undefined, which causes the if condition to fail, so the nested statements don't run. Thus, the scripter is freed from worrying about which specific browsers support the image object.

Implementing object detection on a broader scale can free you from the complexities of today's browser sniffing. For example, a function that switches a style property can work in both the IE 4 and W3C DOM browsers, but requires different referencing syntax for the element. The following function sets the fontWeight style property of an element to bold:

 function emBolden(elemID) {      var elem;      if (document.all) {           elem = document.all(elemID);      } else if (document.getElementById) {           elem = document.getElementById(elemID);      }      if (elem && elem.style && elem.style.fontWeight) {           elem.style.fontWeight = "bold";      } } 

A local variable, elem, is initialized as a null value. The if/else construction looks for the two element reference types that I know have a chance of supporting the style property. The test for document.all is like the earlier example of document.images. Less well-known is that object methods are exposed in most browsers as properties, whose existence can be tested in a similar fashion. Thus, the test for the existence of the document.getElementById( ) method prior to invoking it.

To protect additional script statements from the case of both if/else conditions failing, the balance of the function begins by verifying that elem has a value assigned to it. The tripartite condition is overkill for this specific application, because you can make an educated and safe assumption that any browser that supports either document.all or document.getElementById( ) also supports not only the style property of elements, but also the very common fontWeight style property. But the example is here to demonstrate how to go about verifying the existence of a property when the object or intermediate property may not exist. In the above example, you cannot test simply for the existence of elem.style.fontWeight. A "one-dot" evaluation rule applies to JavaScript, whereby every reference up to the rightmost dot must evaluate successfully in order for the interpreter to see whether the last reference succeeds or fails. If you were to test for the existence of elem.style.fontWeight by itself, and elem was not a valid reference, the script interpreter generates a script error. Evaluation tests of an if condition are conducted from left to right. If any one of the ANDed expressions fails, the condition immediately fails (short circuits), and no further evaluations occur, leaving your browser free from script errors there.

Some browsers, especially older IE, Netscape, and Opera versions, may require more help in evaluating conditional expressions. For these browsers, a value of undefined does not necessarily convert to false (although the ECMA specification says it should). To obtain the same result, you can use the typeof operator to inspect the data type of the object or property:

 if (elem && (typeof elem.style != "undefined")) {...} 

A value of null does correctly evaluate to false for all browsers, so the first test for the existence of elem, is fine the way it is. If elem exists, the string returned by the typeof operator gets compared against undefined. If the data type is anything other than undefined, processing continues (the test for fontWeight is not shown here for the sake of brevity).

Notice, too, that the typeof operator helps in those cases when a property exists and its value (perhaps its default value) is either an empty string or zero. Both of these values would cause the conditional expression to evaluate to false, even though the property exists. By making sure the property value is either a particular data type or anything other than undefined, your condition more accurately reports the presence of the property.

Object detection doesn't solve every compatibility problem, and requires having at hand a good reference of currently-supported DHTML features (such as Dynamic HTML: The Definitive Reference). There are times, particularly when designing around known (and now fixed) bugs in earlier browsers, when browser sniffing is appropriate on a small scale. Yet for a great many scripts, object detection can not only ease implementation of incompatible syntax, but also allow older browsers to degrade gracefully by skipping over code that would generate errors.

Whether you elect to use object detection, browser version sniffing, or a mix of the two, you have a choice of several cross-browser deployment strategies: page branching, internal branching, common denominator design, and custom API development. Additional choices you'll make include whether you wish to deny page access to older browsers, provide multiple paths for browsers of different capabilities, or provide just one path that enhances the experience for DHTML features of your design yet degrades gracefully for those browsers without the latest doodads. The following sections describe some of the more popular strategies for accommodating multiple browsers.

II.4.4. Designing for the Common Denominator

From a maintenance point of view, the ideal DHTML page is one that uses a common denominator of syntax that all supported browsers interpret and render identically. You can achieve some success with this approach if you target W3C DOM-capable browsers, but you must be very careful in selecting standards-based syntax that is implemented identically in all such browsers. Because some of these standards were little more than working drafts as the supposedly compatible browsers were released to the world, the implementations have not been consistent across the board. In other words, just because a browser indicates through object detection that it supports the document.getElementById( ) method doesn't automatically mean it supports everything else in the W3C DOM.

As mentioned earlier in this chapter, one area that eludes common denominator status when serving to modern mainstream browsers is the event model. That Internet Explorer (at least through version 7) does not support the W3C DOM event model means that you need to be fluent in both models. Online Section VI goes into more detail about blending support for both models into your scripts.

II.4.5. Custom APIs

Once you resolve compatibility issues for frequently-used routines, you'll be glad to never have to do it again. To that end, you should keep in mind the idea of creating one or more libraries of general-purpose functions and/or objects that handle the cross-browser issues for you. In a sense, you'll be creating your own meta language for scripted DHTML operations by writing a set of functions that have terminology you design. Place the functions in a .js library file and rely on them as if they were part of your scripting vocabulary. The language and function set you create is called an application programming interfacean API. Groups of APIs are sometimes called a framework. Example II-1 shows a typical function that works around the very different ways that the W3C DOM and Internet Explorer report the computed style sheet property of an element.

Example II-1. API function to obtain a computed style property

 // return computed value for an element's style property function getElementStyle(elemID, CSSStyleProp) {     var elem = document.getElementById(elemID);     var styleValue, camel;     if (elem) {         if (document.defaultView) {             // W3C DOM version             var compStyle = document.defaultView.getComputedStyle(elem, "");             styleValue = compStyle.getPropertyValue(CSSStyleProp);         } else if (elem.currentStyle) {             // make IE style property camelCase name from CSS version             var IEStyleProp = CSSStyleProp;             var re = /-\D/;             while (re.test(IEStyleProp)) {                 camel = IEStyleProp.match(re)[0].charAt(1).toUpperCase( );                 IEStyleProp = IEStyleProp.replace(re, camel);             }             styleValue = elem.currentStyle[IEStyleProp];         }     }     return (styleValue) ? styleValue : null; } 

The getElementStyle( ) function of Example II-1 works around the wide disparity in object models for reading the value of a style property assigned in rules located other than in the style attribute of the element. The W3C DOM provides an interface for accessing the "view" of the current document. This view object (of the AbstractView class) has a getComputedStyle( ) method, which returns a CSSStyleDeclaration object, which contains all computed style property values for an element. To read one of those properties, use the getPropertyValue( ) method, passing the CSS name of the property as a parameter.

Internet Explorer exposes this information entirely differently. Each element object has a currentStyle property, which is an object whose property names are the scriptable names of CSS properties. For example, the CSS font-size property is scripted as the fontSize property. The branch of the getElementStyle( ) function that works under IE uses regular expressions and string replacement to change the CSS property name to its scripted equivalent (crossing one's fingers that Microsoft will continue to adhere to the convention of turning hyphenated CSS property names into "lowerCamelCase" words).

With an API function like this in place, you no longer have to worry about these details and discrepancies. Instead, your scripts invoke getElementStyle( ), passing as arguments the ID of the desired element and the CSS property name whose value you're looking for.

Building an API along these lines lets you raise the common denominator of DHTML functionality for your applications. You free yourself from limits that would be imposed by adhering to 100% syntactical compatibility.




Dynamic HTML. The Definitive Reference
Dynamic HTML: The Definitive Reference
ISBN: 0596527403
EAN: 2147483647
Year: 2004
Pages: 120
Authors: Danny Goodman

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net