Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

DHTML, Standards and Browser Compatibility

4.38/5 (6 votes)
28 Aug 2013CPOL16 min read 1  
This article describes some of the common challenges involved with developing interactive HTML interfaces, along with some ideas for how to solve them. We’ll also take a look at some of the fundamental problems with developing highly-interactive Web interfaces.

This article is written by James Shimada, and was originally published in the August 2005 issue of the Software Developer's Journal. You can find more articles at the SDJ website.

Introduction

As a Web developer, you can go a long way towards enriching the interactivity of your Web applications by making an integrated use of JavaScript, Cascading Style Sheets (CSS), and HTML. Although these technologies have rapidly matured and standards compliance is now taken very seriously among all the Web browser vendors, it can still be very challenging to build a rich, interactive Web application that works seamlessly on many different types of browsers. This article describes some of the common challenges involved with developing interactive HTML interfaces, along with some ideas for how to solve them. We’ll take a look at some of the fundamental problems with developing highly-interactive Web interfaces, and then focus on how we can leverage standards-oriented code to maximize browser compatibility. We’ll also discuss some ideas of how to try and write code that’s forward-compatible with emerging standards.

The Challenges of DHTML

The term Dynamic HTML or DHTML is often used to describe the amalgam of HTML, JavaScript, and CSS technology. It can also refer to the features of a Web page that result in using JavaScript to manipulate HTML content or CSS styling. Indeed, there are several definitions for DHTML (see Google). In fact, the problem of accurately defining DHTML can provide some insight to the challenge of implementing it successfully. As you scan each definition of DHTML, it’s clear that they all mean roughly the same thing, but some go into a little more detail about a particular facet of it than others do. It becomes clear that there are subtly different interpretations of the concept of DHTML. It would probably be difficult to come up with a single description that suits everyone’s idea of what DHTML is, and likewise, it can be difficult to code up a DHTML solution that works the same on every browser out there. In this article, focus on how to address this issue by paying attention to standards compliance, but first let’s mention a couple of other fundamental problems with implementing DHTML.

There’s also the challenge of integrating the client-side DHTML with your server-side systems, which is often where your application logic is implemented. A commitment to DHTML causes you to re-examine the role of a server-side content delivery system and scripting language such as PHP, Perl/Mason, or ASP, which customarily is considered the presentation layer. With DHTML, client-side JavaScript takes on a more active role in delivering the presentation, and care must be taken in defining the roles and interfaces between the server-side front end and your client-side presentation layer. To this end, one of the biggest shortcomings you’ll find with the typical Web developer’s toolkit is that there’s not an easy, obvious way to retrieve data from the server from within a JavaScript function without reloading the entire Web page. Indeed, when your client-side JavaScript UI becomes more advanced, reloading the page can be a major disruption. There are indeed several ways to implement Remote Scripting (JavaScript Remote Procedure Calls), but there is currently no standards-based approach. DHTML can be very challenging from a UI design perspective as well. One of the objectives of DHTML is to provide the means to develop Web-based applications that emulate the richness of a desktop-based application. But there is a fundamental difference between Web UI design and desktop software design: there is not nearly the same amount of design conventions and guidelines that can be leveraged by Web-based software. People rely on familiarity with UI conventions to learn about form expectations of the software they use. For example, even if you’ve never used a particular desktop program before, you can still probably expect that Save will be under the File menu, and you might try right-clicking (or Control-clicking on the Mac) on an icon to discover more contextual commands related to it. Unfortunately, these conventions don’t often exist on the Web. There is no universal concept of a File menu on the Web, nor any menu for that matter. Instead, we are left with disparate concepts of how to interact with an application. When greeted with a complicated Web application, users often need to make a larger initial investment in learning how it works before feeling confident with it. Gratuitous DHTML can sometimes only exacerbate this problem.

So now, we have a good idea about the problems associated with implementing DHTML. First, Web browser support is inconsistent. Second, it requires more complicated interaction with the presentation tier of your application (i.e., your PHP script used to just print out simple HTML, but now it’s got to manage all sorts of JavaScript and other DHTML stuff). Third, it’s difficult to design a successful DHTML-intensive UI because of the lack of UI guidelines. But don’t be discouraged! With the knowledge of what to avoid, along with some sensible design considerations, you can create a rich, satisfying DHTML-based solution that minimizes these issues while meeting the needs of your users.

Know Your Audience

When it comes to solving the browser compatibility problem, the first place to start is with who will be using your application. Is this an application for a corporate intranet? Or will your application be available on the Internet at large? Are there Web server log reports available that can break down traffic to your site by browser and version? If you know what browsers will be in use, then you can design your application with your users’ environments in mind.

However, you might not even be able to predict what browsers your users will have. In this case, you may be able to get a rough idea of your users’ environments by looking at estimations of the overall usage and market share of Web browsers. As of this writing, Web analytics companies such as the Amsterdam-based OneStat.com and WebSiteStory from San Diego have reported that Microsoft’s Internet Explorer has 88.9% usage and 92.9% market share, respectively. This may sound like IE is the overwhelmingly popular choice among browsers, but consider that OneStat’s survey shows IE’s usage has gone down 5 percentage points over a six-month period, and WebSideStory reports a 2.6% drop in its market share over a five-month period. Much of the drop in IE’s dominance is attributed to the rising popularity of Mozilla Firefox. While Firefox has become popular because of its useful plug-in features and security improvements, it also does an excellent job implementing the W3C standards for HTML, DOM, and CSS, making it an ideal target platform for DHTML programmers.

Stick to the Standards

On the one hand, the Web is a platform-independent paradise. You can build an application, it is immediately distributed to your users, and it can be run without any special software (besides a Web browser), on any operating system. That’s the promise at least, and eventually we’ll get there. But in order for that promise to be fulfilled, Web browsers need to implement a standardized version of DHTML. Leading the way to standardization is the global committee for interoperability, the World Wide Web Consortium (W3C). Among other things, the organization is responsible for developing standard specifications for DHTML-related technologies. Although the W3C politely acknowledges the term DHTML, they do not officially use that term in any of their technical literature. Rather, they maintain several specifications and technical reports for HTML (including XHTML, the XML equivalent of HTML), CSS, and the Document Object Model (DOM). For a Web programmer, the DOM technical reports are perhaps the most interesting. W3C describes DOM rather generically as an "...interface that will allow programs and scripts to dynamically access and update the content, structure, and style of documents...". In Web programming terms, DOM describes the standard JavaScript API for manipulating the elements of Web pages, as described in the DOM Core and DOM HTML recommendations. One key feature of DOM is that when you manipulate any of the underlying data of a Web page, it takes effect immediately. This is unlike traditional Web programming, where in order to update some data on the page, you would have to make a request to the server, do some processing, and regenerate the HTML. With DOM, data on a Web page is instantly updateable using JavaScript. You may still need to send the final results of the user’s actions to the server for processing, but in the meantime, you can create a more instantly gratifying experience while saving unnecessary round trips to the server. Listing 1 contains a very simple illustration of using DOM and the ubiquitous getElementById() function.

Listing 1. Simple illustration of using DOM

HTML
<span id="myId">Hi</span>
<br />
<a href="javascript:updateText( 'Hello World' )">Hello World</a>

<script language="javascript" type="text/javascript">
function updateText( newText ) {
    var elem = document.getElementById( "myId" );
    elem.innerHTML = newText;
}
</script>

This example simply updates the contents of the <span> block with Hello World by updating the <span> object’s innerHTML property. The innerHTML property is deprecated by W3C, but it provides for a very straightforward example of DOM usage. innerHTML is an example of a function that’s part of something called DOM Level 0. Before W3C started making recommendations for DOM, Web browser vendors had already made some progress in defining a standardized JavaScript API for HTML documents, and much of it (but certainly not all) conformed to the industry standard JavaScript specification known as the ECMA-Script. The JavaScript functions and properties that were pretty much universally supported among Web browsers before the W3C’s formal recommendations were grandfathered in to the Level 0 DOM. Level 0 DOM functions are generally safe to use for cross-browser compatible scripts but, like innerHTML, may not make it into further W3C recommendations, so support in newer browsers is not necessarily guaranteed.

Standard Headaches

HTML, CSS, and DOM standards should be the saving grace of the compatibility problems caused by various Web browser vendors charging ahead with their own ideas of how a Web browser should work. Indeed, for the most part they are, but it doesn’t mean it’s always easy implementing them. It also doesn’t mean that Web browsers uniformly support all of the standards.

The DHTML programmer is constantly faced with the problem that arises from the need to implement a feature that is not supported consistently, if at all, across all the target browser platforms. There may be older browsers in your user environments that you must support, or modern browsers in your user environments may not uniformly implement an API in the DOM W3C recommendation. But where there’s a will, there’s a way. Let’s look at an example of how to work around a W3C-recommended mechanism that’s not uniformly supported by all the latest browsers.

Opacity: Making up for Inconsistent APIs

One very slick effect for your Web pages is achieved by manipulating the opacity of a page element. By assigning an opacity value of less than 100% to a page element such as an image or a block of text, you can achieve a transparent effect. If you manipulate opacity over time, you can even achieve a fading effect. Even better, manipulating opacity is supported on all kinds of browsers, such as Firefox, IE, and Safari. The downside is that these browsers all implement opacity differently. However, we can still write some relatively clean JavaScript code that can implement opacity on all the different types of browsers. In this example, we’ll write a JavaScript function that fades a block of text into view.

We’ll start with the HTML of the text block that we’ll fade into view. Notice how the display style of the outer <div> container is set to none which will make it invisible, by default. We’ll include a couple of links that, when clicked, will execute JavaScript functions we’ll write to show or hide the <div> block (see Listing 2).

Listing 2. Show and hide <div> block example

HTML
<a href="javascript:show( 'fadeInBlock' )">Show</a>
<a href="javascript:hide( 'fadeInBlock' )">Hide</a>

<div id="fadeInBlock"
 style="display:none; border=1px solid #666666;
 background-color:#DADADA; width:95%; height=12">
<p style="margin:5; font-weight:bold">
This text is fading fading fading in.
</p>
</div>

Now, let’s take a look at how those show() and hide() functions are implemented. The first thing you’ll notice is that both functions take the id attribute as an argument. Each of these functions will use our favorite getElementById method to access the object representation of the <div> block. The hide() function is very simple. It simply needs to make sure that the display style is set back to none. The show() function remains at a fairly high level, but it gives us a good outline for how we approach the problem. It first makes sure that the browser supports manipulating the opacity. If it does, we’ll initially set the opacity to zero, making it completely transparent, so then we can safely set the display style to block. Once the display is in block mode, we can call a function that gradually increases the opacity until it’s solid (Listing 3).

Listing 3. Gradually increased opacity

JavaScript
<script language="javascript" type="text/javascript">
function show( id ) {
    var elem = document.getElementById( id );
    if( supportsOpacity( elem )) {
        // first set 0% opacity to make it completely transparent
        setOpacity( elem, 0 );
        // when we set display = "block" it's still invisible
        elem.style.display = "block";
        // call the function to gradually increase opacity
        fadeIn( id );
    } else {
        // can't modify opacity, so just make it visible...
        elem.style.display = "block";
    }
}

function hide( id ) {
    var elem = document.getElementById( id );
    elem.style.display="none";
}
</script>

The show() function calls three functions that we need to implement: supportsOpacity(), setOpacity(), and fadeIn(). Now, we’ll see how the three different browsers can have three different ways of supporting opacity (Listing 4). Starting with the supportsOpacity() function, we see the same technique that was used to to determine if encodeURIComponent was implemented, by checking for the defined-ness of the various accessors to the opacity style. Mozilla and Firefox support the MozOpacity style, while IE implements opacity using filters. Furthermore, Safari supports the opacity style. The latter is the W3C standard for opacity, but only Safari and Opera do a respectable job of supporting it. It may also be worth noting here that IE can only support opacity through the use of filters if the target element (in this case, the <div> block) has height and width style attributes. This has to do with the way Microsoft defines filters.

Listing 4. Three different ways of supporting opacity

JavaScript
<script language="javascript" type="text/javascript">

function supportsOpacity( el ) {
    if ( el.style.opacity != undefined )
        return true;
    if( el.style.MozOpacity != undefined )
        return true;

    if ( el.style.filter != undefined )
        return true;
    return false;
}

function setOpacity( el, opaciLevel ) {
    if ( el.style.opacity != undefined ) {
        el.style.opacity = opaciLevel;
    } else if( el.style.MozOpacity != undefined ) {
        el.style.MozOpacity = opaciLevel;
    } else if ( el.style.filter != undefined ) {
        var oplvl = Math.round(opaciLevel*100);
        el.style.filter="alpha(opacity=" + oplvl + ")";

    }
}

function fadeIn( id, currentOpacity ) {
    var counterLimit = 20;
    var el = document.getElementById( id );
    if( !currentOpacity ) {
        currentOpacity = 1;
    }
    if( currentOpacity > counterLimit ) {
        return;
    }
    setOpacity( el, ( currentOpacity/counterLimit ) );
    currentOpacity++;
    var func = "fadeIn( '" + id + "', " + currentOpacity + ")";
    window.setTimeout( func, 50);
}
</script>

Evaluating the accessory in the fashion of our supportsOpacity function tells us whether this style even exists, even if the respective opacity style is undefined. If any of the three flavours of the opacity style exists, we conclude that the browser supports it. Next, the setOpacity() uses the same logic except this function actually sets a value to the opacity property. The opacity and MozOpacity style properties accept the same value, a floating point number between 0 and 1, but IE’s filter requires a slightly more cumbersome syntax in addition to requiring an integer value between 0 and 100 for the value of the opacity "filter". Finally, the fadeIn() function uses the setTimeout() method to call itself 20 times at 50 millisecond intervals, each time increasing the opacity by one twentieth.

Hopefully, most of your JavaScript code can be written such that you can take advantage of the commonalities with different browsers. But as this example shows you, sometimes you need to account for differences in the way browsers implement an equivalent function. If you must write JavaScript code that is conditional upon different browser implementations, then it’s usually smart to make your code conditional upon the defined-ness of particular methods or properties, rather than conditional upon the actual browser version. Consider the following alternative implementation of our setOpacity() function (see Listing 5).

Although code like this may have identical results as the other implementations, it has problems that can cause it to break as browsers mature. First of all, it locks IE and Mozilla into their idiosyncratic support for opacity. As soon as the release of Firefox or IE comes out that supports the standard opacity property, wouldn’t you want to use that mechanism instead, because who’s to say that the non-standard means of supporting opacity wouldn’t be deprecated and removed from a future release? Furthermore, since different versions of the same browser can vary wildly in supported features, you’d have to account for the version numbers in order to make the browser-checking code really work. And also, especially with IE, it may behave quite differently on different platforms. For instance, the latest version of IE for the Mac, 5.2, has little or no DOM support. As you can imagine, keeping track of all this in your code would be quite ungainly and difficult to maintain. And you would never want your code to stop working correctly with newer browsers. The most straightforward technique is to check for the methods or properties you want to use, and then build in precedence so the standards-based methods and properties are called before the idiosyncratic ones. Also, consider using higher-level wrapper functions such as setOpacity() that figure out the details of the particular browser API. Then, at least the code that uses the wrapper functions can remain browser-independent.

Listing 5. Alternative implementation of the setOpacity() function

JavaScript
function setOpacity( el, opaciLevel ) {
    // assume getBrowserType returns "moz", "ie", "safari" or "opera"
    thisBrowser = getBrowserType(); 
    if( thisBrowser == "moz" ) {
        el.style.MozOpacity = opaciLevel;
    } else if (thisBrowser == "ie" ) {
        var oplvl = Math.round(opaciLevel*100);
        el.style.filter="alpha(opacity=" + oplvl + ")";
    } else if ((thisBrowser == "safari") 
            || (thisBrowser == "opera")) {
    el.style.opacity = opaciLevel;
    }
}

Remote Scripting: One step ahead of the standards

It’s a shame when even the latest versions of popular Web browsers seem to be one step behind the W3C standard DOM. The only thing worse than that is when the W3C standard is one step behind the features you really want to implement in your Web application. Remote Scripting, or using client-side JavaScript to access data on the server without reloading the page, might be the best example of a high-demand feature with no standards support.

Until it becomes part of the standard, Web browser vendors respond to the demand with their own DOM extensions for Remote Scripting, by exposing something akin to an XMLHTTP Request API. You can even find an Open Source JavaScript library that implements a common interface to an XMLHTTP Request object in the Sarissa library written by Emmanuil Batsis. Like our setOpacity example, Sarissa takes care of all the browser-specific details, and provides the programmer with a high-level API for Remote Scripting, as well as XSLT and XPath tasks that can run on a variety of different browsers.

But even before any browsers implemented an XMLHTTP Request API, pure JavaScript techniques were in use to perform Remote Scripting. While it doesn’t seem like it would be possible to use only JavaScript to access data from the server without reloading the page, by making clever use of the DOM, you can emulate a JavaScript RPC. One of the most widely-used JavaScript libraries for performing Remote Scripting is the JavaScript Remote Scripting Library (JSRS), written by Brett Ashely and distributed under an Open Source license. In a nutshell, it crafts an RPC by using DOM to create an inline frame. The inline frame has a src attribute of the server-side script. When the inline frame loads, your client-side script has access to the data contained in it.

Projects like Sarissa and JSRS show that even when standards support does not exist for a particular feature, and you’d like to incorporate it into your Web application, you can still do a reasonably good job implementing that feature. By leveraging other people’s work or implementing your own interface to a DOM workaround, it’s still possible to achieve the goal of cross-browser compatibility while saving most of your code from the rigor of understanding how it’s accomplished with each different browser.

Conclusion

Web browsers have made great strides in implementing standardized DHTML-related technologies, but as we’ve seen, there are still plenty of challenges with implementing DHTML solutions that work across different browsers. But things are only getting better from here, as standards compliance is taken more seriously and new browsers like Firefox are starting to shake up the browser market. Most importantly, you can save a lot of time and effort if you design your DHTML applications with standards in mind, to begin with. Try to take advantage of commonalities of your target browsers. But if you are faced with reconciling different browser APIs, strive for higher-level abstractions of the functionality that hide the browser-specific details.

Resources

Be sure to check out the following resources for an in-depth information on browser-compatible DHTML:

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)