Chapter 3. Integrating the Behavior Layer

Capturing, reacting to, and performing actions based on users’ interactions with Web pages is the realm of the “behavior layer.” While HTML and Web browsers have basic responses to “behaviors” such as clicking a link or submitting a form, today these responses can be enhanced radically through the use of scripts sitting on top of the markup. The scripts respond to logical decisions made by users, thereby creating a responsive user interface (UI) and controlling what and when (or even if) anything is actually sent to the server. Modern scripts can even control a UI by receiving data from the server and modifying an existing page without a full page refresh.

In a Web standards world, scripts and the behavior layer involve the careful application of JavaScript to enable both an event layer and the appropriate reactions to users’ actions. JavaScript needs to be consistent, well organized, and respectful of the front-end presentation and structure layers.

In the past, JavaScript served a simple purpose: providing “stupid Web tricks” with varying degrees of usefulness. Today, with full Document Object Model (DOM) support, Asynchronous JavaScript and XML (Ajax), and richer experiences possible, JavaScript is getting more attention. It is moving beyond its past as a cut-and-paste language and is being adopted by large frameworks with very object-oriented structures and mature design patterns.

In some large organizations or complex environments, JavaScript is frequently applied as part of a front-end solution as well as the backend applications, yet it is frequently implemented by an altogether different team.

In most cases, except for more complex Rich Internet Applications (RIAs), modern scripts should be enhancements to existing pages and be laid over the top in a way that is not browser-specific and will allow the page to operate even if the user has scripts disabled or some other issue prevents them from executing.

While the percentage of users browsing the Web without scripts is small, one of the overarching tenets of modern best practices is that all types of users should have access to content, including users who have scripts disabled. This tenet also applies to the edge case where the site is developed for a specific browser and not built with open standards. Pages that rely on scripts for fundamental features and functionality, even items as mundane as links, go against this ideal and can cause problems and potentially irritate users who don’t understand why something might not be working.

Modern Ajax Methods

In February 2005, Jesse James Garrett of Adaptive Path published an article discussing new techniques in Web scripting that had largely been operating under the radar. (His article is located at adaptivepath.com/publications/essays/archives/000385.php.)

Garrett’s article, called “Ajax: A New Approach to Web Applications,” defined Ajax (short for Asynchronous JavaScript and XML) as a new platform for Web technologies using JavaScript features to send XML communications from a Web page in the background, without interruption, to the server, without refreshing the browser window. There had been hacks and other workarounds for effects like this for years involving things such as hidden <iframe> elements.

While the core technologies he discussed had been around since the late 1990s, Garrett’s article, combined with modern browser support, suddenly set off a virtual scripting revolution in Web development. When application developers realized JavaScript could function in ways desktop software could, with data interchange in the background, without users waiting for a form to be posted and responded to, JavaScript suddenly got tons of attention—and much more respect.

The term Ajax (also seen as AJAX) is defined in many different ways these days: from the strict background XML data interchange to a broad modern user experience featuring page updates, drag-and-drop, fades, animations, and other more desktop-software-like effects (an example of which is shown in FIGURE 3.1). In this sense, it describes the use of modern browsers with robust JavaScript support on faster and more advanced computers, combined with more prominent broadband access and traditional strict Ajax techniques.

Apple’s Web Gallery, a feature of its .Mac service, makes extensive use of Ajax.

Figure 3.1. Apple’s Web Gallery, a feature of its .Mac service, makes extensive use of Ajax.

Whatever the definition, it is clear that Ajax has brought specific attention to the techniques supported by JavaScript in modern browsers. Combined with Web standards-based approaches, it is quite a powerful “experience delivery” tool.

Web applications such as Google’s Gmail (www.gmail.com, FIGURE 3.2) and Meebo’s Web-based chat clients (wwwl.meebo.com, FIGURE 3.3) are helping to popularize Ajax-based techniques. They feature real-time, no-page-refresh applications that in many ways mimic desktop software behaviors. One example in Gmail is a background auto-save feature as you type the draft of your email message. Likewise, Meebo features a chat window where the conversation is updated in the screen without a refresh—it can also be dragged around the screen just as on the desktop. These basic, well-known features are powered by Ajax, and technically, under the hood by JavaScript.

Google’s Gmail service uses an enormous amount of Ajax and JavaScript.

Figure 3.2. Google’s Gmail service uses an enormous amount of Ajax and JavaScript.

Online chat service meebo.com also uses generous amounts of Ajax and JavaScript.

Figure 3.3. Online chat service meebo.com also uses generous amounts of Ajax and JavaScript.

Modern, Progressive, and Unobtrusive Scripting

Most experts today agree that modern JavaScript needs to move far beyond its early history of browser-specific techniques and inline code mixed within the HTML markup of the Web site.

Some ways modern scriptwriters have been doing this include

  • Adopting a new progressive pattern and philosophy in which all scripts are “unobtrusive”

  • Keeping the scripts’ code separate from the content and presentation layers of the Web site, in external files where possible. If scripts were not separated, then the strict separation of content and presentation would lose its effectiveness. Unobtrusive scripts do not “intrude” into the other layers.

  • Creating more mature, scalable, reusable, object-oriented code

  • Using cross-browser feature sets rather than browser-specific proprietary ones

  • In browsers visiting Web pages where the scripts do not work for some reason, making sure the Web pages still work and degrade gracefully

JavaScript has really entered a time and a state of organization, discipline, and responsibility. This is a far cry from the outmoded description given in Wikipedia: “a clumsy, hackish language unsuitable for application development” (http://wikipedia.org/wiki/Unobtrusive_JavaScript). Much of this bad reputation is due to its marketing history and the coders writing JavaScript, but that is changing. Doug Crockford, a frequent speaker on JavaScript, years ago wrote that JavaScript was “the world’s most misunderstood programming language” (www.crockford.com/javascript/javascript.html).

JavaScript has earned the world’s respect for sure.

JavaScript Requirements: File and Function Inventory

Where the front end and the backend come together and interact with presentation and content—that’s where the scripting layer comes in. And in that important place, it takes discipline to remove chaos, limit code bloat, and eliminate risk of inconsistent implementations. With more scripts comes a bigger need for Web teams to bring consistency and agree on internal standards and requirements. A more disciplined approach means creating site-wide requirements; an inventory of scripts, what they’re for, how to get to them; and even rules for writing new scripts.

Many larger organizations with legacy code are faced with massive collections of scripts that are growing out of control. A common scenario is to see five different pop-up window scripts, a massive but flawed browser-detection script, and nowadays even several new Ajax scripts that do the same thing but are repeated and all downloaded to the end user. Additionally, there may be dozens of inline document.write statements or other functions that modify the UI with font sizes or colors. This is difficult to manage and violates the separation of the independent layers of a given design.

As with any software, an evaluation should be made of all the required functionality, a core set of functions created or agreed upon, and a standard way of interacting with UI elements created. Scripts should be stored in a central location and documented by a knowledgeable staff that is aware of what is available and what is needed.

A reference for a JavaScript function can be just like any other programming language. For instance, if there was a utility function to combine two strings together (not that someone would ever need such a thing), it might be documented as follows:

<LINELENGTH>90</LINELENGTH>
string myStringCombiner(string param1, string param2)
   Purpose: combines two strings, returns a new string
   Parameter  Description
   param1     Required. First string to concatenate to the second.
   param2     Required. Second string to concatenate to the first.
Notes: Function is available by including base-functions.js.

Bad Script, Bad

So what does modern, Web standards-respectful JavaScript code look like?

Arguably, a good way to show what modern code can look like would be to start with what not to do. Unfortunately, the bad code will look quite familiar.

The next few sections will start with the beginnings of a source document that includes some JavaScript and work through the code section by section, taking note of the serious problems that are demonstrated.

This document is fairly close to nonsense, of course—certainly not an example of good, modern scripting. With that in mind, a few years ago this would not have been an unusual sight (FIGURE 3.4):

<LINELENGTH>90</LINELENGTH>
<html>
<head>
   <title>Old Skool JavaScript</title>
   <script language="JavaScript">
   <!-- // cloaking device
   b = navigator.appName
   function callMe(){
      if (b.indexOf("Netscape") >= 0) { // detect Netscape
         alert("Sorry, this site is optimized" +
               " for Internet Explorer!")
      }
      else { alert("We welcome you:") }
   }
A meaningless JavaScript or “DHTML” screen.

Figure 3.4. A meaningless JavaScript or “DHTML” screen.

As the page loads, it performs a poorly thought-out browser detection, alerts users of both Internet Explorer and Netscape (as near as it can determine), and then sets up the user for a series of actions. These are the first offenses (we’ll number them as we go along):

  1. The script features a very weak user-agent-based browser-detection script for questionable results.

  2. The page appears to be targeted at Microsoft’s IE browser and assumes that the only other browser coming to the site is going to be Netscape, which in today’s world is extremely unlikely. Today’s scripts should never assume the audience is using a specific browser unless in a controlled environment.

  3. The language attribute of the <script> tag is not required, and the type attribute is missing.

    Moving further into the document:

    <LINELENGTH>90</LINELENGTH>
      function goThere(){
         window.open("http://cherny.com","myWindow",
            "height=500,width=500,scrollbars=yes")
      }
      function goHere(){
         window.open("http://www.google.com/","myWindow",
            "height=400,width=400,scrollbars=no")
      }

    Here we have two functions that do virtually the same thing, although this could be handled with a single, more robust function.

  4. The script uses pop-up windows and has two functions to do this, when it could use one (whether or not this should be done in the first place).

    More of the code:

    <LINELENGTH>90</LINELENGTH>
      function hookUp(){
         var x = document.all.tags("p")
         var el = x[1]
            el.style.cursor = "hand";
            el.style.width = "150px"
            el.style.height = "100px";
            el.style.borderColor = "red";
            el.style.borderWidth = "1px"
            el.style.borderStyle = "solid";
      }
      var on = false;
      function update(){
         var x = document.all("myDHTML")
         if (!on) {
            x.style.width = "400px";
            x.style.height = "auto";
            x.style.borderColor = "green";
            on = true;
        }
        else {
           x.style.cursor = "hand";
           x.style.width = "150px"
           x.style.height = "100px";
           x.style.borderColor = "red";
           x.style.borderWidth = "1px"
           x.style.borderStyle = "solid";
           on = false;
         }
      }
      //-->
      </script>
    </head>

    The preceding section is full of problems:

  5. The script uses Microsoft’s proprietary document.all DOM technique, which will work only in IE. Modern scripts should avoid using proprietary techniques unless absolutely necessary.

    Separation of content from presentation places design into CSS; the above code violates this practice by placing presentation information into the JavaScript code. While scripts may need to make changes to page appearance, they should reference CSS classes, just like markup can. Design updates to the code above would be difficult at best and sabotage the code separation of CSS.

  6. The script in several places modifies .style properties, violating the separation of layers. Sometimes this can be necessary, but certainly not here.

    Modern scripting also means that scripts can and should be linked from external files.

  7. The full script is located in the <head> of the document, when it should be in an external file.

    Now, the rest of the document body:

    <LINELENGTH>90</LINELENGTH>
    <body onload="alert('We like Internet Explorer!'), hookUp()">
    
    What would you like to do now?
    <p>
    - <a href="javascript:goThere()"
      onclick="if (document.all){ alert('Do not leave my Web
    site!')}">
      Go to this site</a>?
    <br>
    - <a href="#" onclick="goHere()">Or, maybe here instead?</a>
    <br>
    - <a href="#"
      onmouseover="document.all('myDHTML').style.visibility =
      'visible';">
      Do you like DHTML?</a>
    <p onclick="update()"
      id="myDHTML"
      style="visibility:hidden;">
      This is my clickable DHTML box which changes size and shape.
      Try it! Click more than once!
      (Fun, right?)</p>
    
    <script language="JavaScript">
    <!--
    callMe()
    //-->
    </script>
    
    </body>
    </html>

    The first two links open pop-up windows. The last is a DHTML box that appears on mouseover to users of IE, and is clickable. When clicked, the box changes size and color. Quite pointless, but it will demonstrate a number of things for us.

    From a modern, progressive standpoint, the above code is atrocious. Although many of its “offenses” were specified in context above, the rest of the problems are general:

  8. The HTML features several inline event handlers, which mixes behavior-related code directly inside of the content and structure. Event handlers should be dynamically wired up when the page loads using the DOM.

  9. The first link features the javascript: pseudo-protocol and no actual URI link. This type of link is nonstandard and can cause accessibility issues. No clicks should ever rely on the javascript: pseudo-protocol.

  10. The second link features the # href attribute with an inline onclick handler and no actual URI that users without scripts can use. All links on a page should have a real-link URI. If it is a script-dependent issue, then the page objects should be added by the script.

  11. The script also features inline logic, again using the document.all proprietary DOM method. All logic should be in external files.

Not to mention all the calls to alert() are just annoying to any user. There may even be more—frankly, it’s hard to keep track of them all. Of course this document is nonsense, but a site may need to hook links up to some scripts and provide interactive widgets such as those on this page. Note also how poor markup decisions are involved directly in the poor scripting decisions. It is all interrelated.

Better Scripts

Today’s best practices recommend a number of techniques to help improve the quality of JavaScript and how it interacts with the UI layer. As previously indicated, these techniques are often referred to as “unobtrusive” JavaScript. Some guidelines for unobtrusive JavaScript include

  1. Use W3C DOM techniques, as opposed to proprietary DOM methods, in order to target Web standards-supporting browsers.

  2. Avoid targeting scripts at specific browsers unless absolutely necessary.

  3. Use external files where possible, so as to avoid inline code in the XHTML.

  4. Avoid inline event handlers.

  5. Avoid javascript: pseudo-link protocol usage.

  6. Use object detection, as opposed to browser detection, if possible.

  7. Create references to presentation elements through classes and IDs or through distinctly separated, easy-to-modify code, if possible.

  8. Pages should work without scripts, if possible, and in older browsers.

  9. Dynamic elements, which are meaningless to users without scripts, should be added by the scripts.

Consider these guidelines in terms of the functionality that is required for your site. Modern browsers allow a pure separation of script from other layers, which can also improve the quality of the code. Additionally, scripts for links and other major functionality should never be tied to specific browsers unless there is a compelling reason, and there must be fallbacks in case users have scripts disabled or are using a different browser than expected. These days there are too many browsers to target specific vendors (except in extreme compatibility conditions). Using Web standards allows a single approach to maximize compatibility, because new browsing technologies will use those standards as their starting point.

Unobtrusive Improvements

There are several ways the badly scripted page above can be brought up to speed:

  1. Convert pseudo-links to actual links.

  2. Remove scripts to an external file.

  3. Convert invalid HTML to semantic and valid code.

  4. Add dynamic elements via scripts.

  5. Hook up event handlers dynamically.

  6. Remove browser detection, since there is no reason to have it.

  7. Remove proprietary techniques and replace them with DOM standards.

External Files and Dynamic Hookups

A big issue above is the inline <script> tags. In some cases they may be difficult to avoid, particularly in more complex environments where third-party code is being inserted, but where it can be done, such as with the scripts at the top of the example document above, scripts should be external files—for the same reasons that CSS files are better kept separate. Benefits include cleaner code, increased modularity, code sharing, and, when combined with other unobtrusive techniques, the ability to change the code in one location instead of having to plow through the content’s entire markup. A compelling enough reason is that it helps enforce the purity of the separation of the documents layers.

A valid external <script> tag looks like this:

<LINELENGTH>90</LINELENGTH>
<script src="unobtrusive.js" type="text/javascript"></script>

Now, having moved the code outside of the document, what about all the references to that code? To obtain pure separation of content from behavior, all the inline event handlers need to go. Additionally, because the javascript: pseudo-protocol is nonstandard, those will be removed and replaced with their actual links (they are pop-ups; more on that in a bit). Also, the links that were in the javascript: function get pushed back down to the HTML for compatibility’s sake. Finally, the “DHTML” aspects of the page, which are fairly meaningless if the user does not have JavaScript, are removed, to be added dynamically later.

With some cleaner, more “POSH” HTML as well, the code would look like this:

<LINELENGTH>90</LINELENGTH>
<body>
<p>What would you like to do now?</p>
<ul id="myUL">
   <li><a href="http://cherny.com">Go to this site</a>?</li>
   <li><a href="http://google.com">Or, maybe here instead</a>?</li>
</ul>
</body>

Notice that with all the event handlers removed, the code has achieved a separation of behavior from content and structure. So how is the code actually executed? In most cases, modern Web developers attach events and behaviors to a page on load, just not with a body onload attribute.

In unobtrusive.js, the scripts need to begin with this form:

<LINELENGTH>90</LINELENGTH>
window.onload = function(){
   // our code will go here, added in the next few sections
}

This simple item of code allows the same functionality as the body onload attribute. Not only will all the old code that was to execute on load be put here, but new calls to set up the other functionality that used to be inline will be put here as well. The modifications to the code will be further explained as the parts are improved one by one in the following sections.

No Proprietary Code

One of the biggest issues in older scripts (and in this example) is reliance on proprietary vendor techniques such as document.all, which has limited browser support beyond Internet Explorer. Web standards support open, nonproprietary technologies that work across browsers, and there are certainly alternatives defined by the W3C in its Document Object Model. Two of these alternatives are

  • document.getElementById(id)

    Returns an element when passed an id that is the ID attribute of the element; the ID needs to be unique in the document.

  • element.getElementsByTagName(tag)

    Returns a nodeList (JavaScript array) of the matching elements by tag.

There are several examples above where document.all can be seamlessly swapped out with document.getElementById. Replace the following:

<LINELENGTH>90</LINELENGTH>
var x = document.all("myDHTML")

with this:

<LINELENGTH>90</LINELENGTH>
var x = document.getElementById("myDHTML")

And replace this:

<LINELENGTH>90</LINELENGTH>
var x = document.all.tags("p")

with this:

<LINELENGTH>90</LINELENGTH>
var x = document.getElementsByTagName("p")

These two methods are key to W3C-based DOM scripting.

Pop-Up Windows

Most experts today recommend against opening links in external windows. There are a variety of opinions on the matter, but with modern browsers that support tabs, a huge volume of third-party add-ons that enable pop-up blocking, and a general desire to cater to users who find multiple windows confusing, pop-up windows are losing favor. Additionally, from a technical standpoint, the old browser-based (nonscripted) method of opening windows with the target=_blank” method is not valid: The target attribute is deprecated in XHTML transitional and removed altogether from XHTML strict.

However, the bottom line is that in many massive corporate environments these arguments are still difficult to win; management typically wants users who are leaving the corporate site to be directed to a new window in an attempt to keep the main site in the background. Compelling content might keep a user there, but that’s another discussion. There are many techniques to get around pop-up windows, but creating them will be used here as an example of adding dynamic event handlers to elements on a page that need actions hooked up to them—and can degrade gracefully.

Adding Event Handlers and Scripting with Class

The first step in setting up proper event-handling was moving the links’ addresses out of the JavaScript and into the href attribute to create real links as a part of the conversion to more “POSH” HTML above. JavaScript developers should always use real links when possible, because of usability and accessibility issues. Dynamic behaviors should be an enhancement to a page rather than a necessity. The conditions where dynamic behaviors might be considered a necessity is when there are alternatives, there is a controlled environment, or the functionality is not required.

One way of adding dynamic functionality to a page with an effective separation of structure and behavior is to assign a CSS class to elements that require scripting to be attached to them. Think of the elements with that specific class value as a defined type of object on a page, and methods, events, and behaviors are assigned to them with JavaScript. This “class” notion is in keeping with the object-oriented term class, which programmers use and which defines a type of object collection with properties and methods. The CSS class may or may not have presentation information referenced in the CSS. If there is no presentation information, it is safely ignored by the CSS, but can still be used by the JavaScript.

To use classes effectively, JavaScript authors often use a script function called “getElementsByClassName()” which, like getElementsByTagName(), returns a collection or array of elements. In this case, those returned have a given class assigned to them. Searching through an entire document can be resource intensive, so these searches are often limited by a tag or ID.

Here is an example script that finds elements in a document with a class name, limited to a specific tag:

<LINELENGTH>90</LINELENGTH>
/* LF = low fidelity: this thing is basic,
 *      folks, no spaces, a single class, etc.
 * there's plenty other good ones out there,
 *      this is for demo purposes only...
*/
function getElementsByClassNameLF(tagName,aClass){
   var z = [];
   if (!document.getElementsByTagName) return z;
   var x = document.getElementsByTagName(tagName);
   for (var i = 0; i < x.length; i++){
      if (x[i].className == aClass) z.push(x[i]);
   }
   return z;
}

An important feature to note is the check for !document.getElementsByTagName, as this checks for support of an important object method which, if not supported, will cause the script to give an error. Make sure the script is linked from the document.

Now, classes and event handlers can be added to the two links without having the code inline in the document.

First, the new links, with a class of “external”:

<LINELENGTH>90</LINELENGTH>
   <li><a href="http://cherny.com" class="external">
         Go to this site</a>?</li>
   <li><a href="http://google.com" class="external">
         Or, maybe here instead</a>?</li>

Second, the new window.onload uses the getElementsByClassNameLF():

<LINELENGTH>90</LINELENGTH>
window.onload = function(){
   // find a elements that have an 'external' class
   var linksExt = getElementsByClassNameLF("a","external");
   if (linksExt.length > 0){
      for (var i = 0; i < linksExt.length; i++){
         // add onclick handler to each!
         linksExt[i].onclick = function(){
            window.open(this.href,"ourExternals");
            return false;
         }
      }
   }
}

Adding classes to items in the document is just one way of adding functionality. Expert Web developers have the full range of W3C DOM methods and properties available to them, including walking the document tree and simply locating document IDs directly.

One last thing to note on the pop-ups: Notice how the window.open references this.href. Because links[i].onclick references the current item in the collection of returned links, this.href is a reference to that link’s href attribute. This allows the links to stay in the document, and simply be used by the script via the event handler that is dynamically created.

Note

The Web Hypertext Application Technology Working Group (WHATWG) has included a powerful version of getElementsByClassName() in its Web Applications and HTML5 specifications, and this functionality is due to be included in Firefox 3. As with much of the WHATWG’s specs, only time will tell if the W3C HTML Working Group includes this in its future work.

The W3C defines a number of ways to add event handlers to page objects. The bottom line is that inline event handlers are the method least recommended by modern scripting gurus: Don’t use it unless there is a compelling reason. The W3C DOM defines a standard addEventListener() method. Unfortunately, Internet Explorer doesn’t support this method, and so you should look online for compatibility functions that will help bridge the gaps in support. Just do a search on something like “attach JavaScript event listener.”

Note

It is important to note that the example script here is for demonstration purposes only and does not have all the features of some functions by that name. Search online for other, more powerful examples that support multiple and combined classes; you’ll find many useful results, including an excellent script from Robert Nyman and Jonathan Snook (www.robertnyman.com/2005/11/07/the-ultimate-getelementsbyclassname/).

Tip

A great place to start on cross-browser event handlers is Scott Andrew LaPera’s now classic “addEvent()” and “removeEvent()” functions posted all the way back in 2001 (www.scottandrew.com/weblog/articles/cbs-events)

Dynamic Elements and innerHTML

The remaining step in improving the sample page is to add dynamic elements after the page has loaded. Unless the elements of the document can serve some purpose when scripts are disabled, it may be best to add the elements after the page has loaded.

The W3C DOM includes a whole series of functions for inserting content and markup into a document. These are highly effective; however, for expediency, Microsoft introduced a proprietary DOM extension called innerHTML that inserts snippets of code into a document. As fate would have it, this method is not only faster to code, but performs faster in the browser as well. The benefits were difficult to deny, so every other major browser vendor that supports W3C DOM standards-based code has implemented this feature. That makes it exceptionally safe and convenient to use. Although some purists balk at its use because it is considered nonstandard, for our purposes here it is too convenient not to use.

Note

Just like getElementsByClassName(), innerHTML is part of the WHATWG’s specifications offered to the W3C HTML Working Group. As with all its recommendations, only time will tell if it is standardized.

In the case of our sample page, there is an object (a link in the original) that on mouseover shows a box that can be resized (for whatever reason), and the box changes its border color when clicked. This object will be inserted dynamically, as it is meaningless without scripts.

Tip

Because content is being inserted into the DOM of the document, this code needs to be placed before the code that was discussed above in the window. onload for the pop-up. This is because when DOM modifications happen, event handlers can be lost! There are ways around this; try searching online for “javascript event delegation” for some helpful links.

So here is some code that can insert the needed DHTML trigger to show the clickable box:

<LINELENGTH>90</LINELENGTH>
window.onload = function(){
// code below here is new
// check if the needed functions are available
if (!document.getElementsByTagName) return false;

// object that will be hidden and shown
var str = '<p class="' + LaF.classInit + '" id="' +
     LaF.classId + '">This is my clickable DHTML ' +
     'box which changes size and shape. Try it! ' +
     'Click more than once! (Fun, right?)</p>';

// add it to the body of the doc
document.getElementsByTagName("body")[0].innerHTML += str;

// add trigger to list that will show the object onmouseover
var ul = document.getElementById("myUL");
if (ul){
   ul.innerHTML += '<li><span class="' + LaF.classLi +
      '">Do you like DHTML?</span></li>';
   var myLi = getElementsByClassNameLF("span",LaF.classLi);
   if (myLi.length > 0) {
      myLi[0].onmouseover = function(){
      var sp = document.getElementById(LaF.classId)
         sp.className = LaF.classOff;
      }
   }
}
// add code to toggle the object when clicked
var box = document.getElementById(LaF.classId);
if (box){
   box.onclick = function(){
      box.className = (box.className == LaF.classOn) ?
         LaF.classOff : LaF.classOn;
   }
}
// code above here is new
/*
 * The modifications of the DOM above remove the Event Handlers
 * which we so carefully attach...sweet. so these need to happen
 * after any DOM mods. event delegation would resolve this.
 */
// find a elements that have an 'external' class
var linksExt = getElementsByClassNameLF("a","external");
if (linksExt.length > 0){
   for (var i = 0; i < linksExt.length; i++){
      // add onclick handler to each!
      linksExt[i].onclick = function(){
         window.open(this.href,"ourExternals");
         return false;
      }
   }
}
}

There is a lot of code here, and it is beyond the scope of the discussion to explain all of the nuances and techniques used in this JavaScript; however, there are a number of things worth pointing out:

  1. The code uses several functions introduced already, including document.getElementsByTagName, document.getElementById, and getElementsByClassName.

  2. A string variable is created that is the HTML for the box that’s being hidden and shown. It’s then appended with innerHTML to the body of the document.

  3. An item is added to the list to be the “Do you like DHTML?” trigger to show the box. A <span> element can be used instead of an <a> anchor element because there is no actual link (it’s never going to take anyone anywhere).

  4. An event handler is added in the code to change the CSS class of the box when the user passes her or his cursor over the trigger <span>.

  5. Finally, the box that was added gets an onclick handler to also toggle its CSS class.

It is also worth noting that there is a little more JavaScript required for this, as well as some CSS. Notice how the script references the CSS classes via variables?

JavaScript Behavior with CSS and Presentation

In the purest sense, if the JavaScript is going to be pulled out of the markup, and the presentation information all lives in external files, shouldn’t the CSS be pulled out of the JavaScript as well? That can happen only to an extent, and in this case there is a demonstration of the presentation aspects of the JavaScript code now being referenced by the classes in the CSS. The names of the class do appear in the JavaScript, just as they appear in the markup of an XHTML document. The classes and IDs are what bind everything together, in a manner of speaking. So in this case, the classes were pulled into a simple JavaScript object literal, which could be referenced and changed easily without plowing through any of the logic in the JavaScript.

<LINELENGTH>90</LINELENGTH>
// Look and Feel object to pull
//    superficial properties out of code,
//    especially classes
var LaF = {
   classExt : "external", // external link class from prior example
   classLi : "trigger",
   classInit : "set",
   classOn : "on",
   classOff : "off",
   classId : "myDHTML"
}

These are easily then referenced like this:

<LINELENGTH>90</LINELENGTH>
LaF.classLi // the class for the trigger to show the box

Note

For a more pure approach, a developer might code the JavaScript in a more object-oriented way. This might be to create a series of objects for each “thing,” where each has its own CSS properties to be inserted into the document and add its own event handlers. This would separate and compartmentalize the code in the JavaScript even more from a maintenance and scope perspective.

For the most part, and in the purest sense, JavaScript should not directly call the .style property of any objects it is manipulating, so that the look and feel of those objects may still be controlled via CSS. A lot can be done by setting up different CSS classes, and those different CSS classes should be leveraged where possible, in order to maintain the purity of the separation of layers in Web standards-based code. As in any situation, there are exceptions, degrees, and nuances that may make it difficult to be 100% pure at all times; however, as with any Web standards-based best practice, there are ideals to aspire to and to weigh against business requirements.

A final note on the remaining CSS: A file is created and linked to the cleaned-up document that includes some rules making the trigger <span> look just like links and to distinguish the two states of our changing box.

<LINELENGTH>90</LINELENGTH>
a, span.trigger {
   text-decoration: underline;
   cursor: pointer;
   color: blue;
}
.off {
   cursor: pointer;
   width: 150px;
   height: 100px;
   border: 1px solid red;
}
.on {
   cursor: pointer;
   width: 400px;
   height: auto;
   border: 1px solid green;
}
.set {
   visibility: hidden;
}

This keeps the presentation aspects of the page in CSS, where they belong. The final document is essentially the same as the original, but without the browser detection and alerts, which served no constructive purpose (FIGURE 3.5).

The improved, unobtrusive script page.

Figure 3.5. The improved, unobtrusive script page.

Say, for example, however, that the span.trigger class needed to be renamed to span.toggle for some reason. Then, using the JavaScript object literal, it is very easy to locate and make that change, rather than having to plow through the JavaScript logic:

<LINELENGTH>90</LINELENGTH>
// Look and Feel object to pull
//    superficial properties out of code,
//    especially classes
var LaF = {
   classExt : "external", // external link class from prior example
   classLi : "toggle",
   classInit : "set",
   classOn : "on",
   classOff : "off",
   classId : "myDHTML"
}

Tip

There is an enormous amount of new work being published online with regard to modern JavaScript design patterns and object-oriented techniques. Try searching online for “javascript object literals,” “object oriented javascript,” and “javascript closures” for some examples. Or, download a framework such as those mentioned in the next section and examine the source—it’s all there for the taking!

Large Sites and Support for Multiple OnLoads

On larger sites, there are likely to be any number of things that need to be added to the page when it’s loaded. This can lead to conflicts because there may need to be more than one onload JavaScript event handler:

<LINELENGTH>90</LINELENGTH>
window.onload = function(){
   // might be a couple of these?
}

There are several ways to deal with this. In a highly controlled environment, a team can aggregate all these calls into a series of setup functions:

<LINELENGTH>90</LINELENGTH>
function setup(){
   productSetup();
   salesSetup();
   uiSetup();
}
window.onload = setup;

Each team member who needs something executed on load would have his or her code added to the setup() function. This can be difficult to manage. The problem gets more complicated when some rogue developer tries to attach an onload event to the <body> element, causing conflicts and events not firing.

Another way would be to create a function that will aggregate load events, which programmer Simon Willison did and posted on his blog at http://simonwillison.net/2004/May/26/addLoadEvent/.

<LINELENGTH>90</LINELENGTH>
function addLoadEvent(func) {
   var oldonload = window.onload;
   if (typeof window.onload != 'function') {
      window.onload = func;
   } else {
      window.onload = function() {
         if (oldonload) {
            oldonload();
         }
         func();
      }
   }
}

Willison’s code above allows multiple load events to be queued up in sequence and fire in order. To test this, all that is needed is a simple document that links to a JavaScript file that includes the addLoadEvent() function, with some additional code:

<LINELENGTH>90</LINELENGTH>
// global variable (typically, avoid this)
var x = "- Default value.";

// define first
function customFunction(){
   // add to the default value to the global var
   x += "
- This is added to the string first.";
}

// call first
addLoadEvent(customFunction);
addLoadEvent(function() {
   // add to the default + first to the global var
   x += "
- This is added to the string second.";
});
addLoadEvent(function(){
   // alert the string showing the calling order
   alert(x);
});

When the page loads, the browser shows the order in which the functions have been called (FIGURE 3.6).

With the right scripting techniques, multiple onload events can be executed.

Figure 3.6. With the right scripting techniques, multiple onload events can be executed.

Whatever page-loading strategy an organization picks should be consistent.

Custom Scripts vs. Frameworks

Depending on the experience of the team, resources, and requirements of a Web project, many different JavaScript options exist. Sometimes only basic scripts are needed and these can be found easily online and extended; however, the team should be exceptionally careful with these, because there are mountains of poorly written JavaScript code online. Additionally, grabbing large volumes of code from varied sources can be risky and result in

  • Inconsistent quality and browser-compatibility issues

  • “Frankenstein”-like sets of functions that use different techniques and styles or repeat core functions unnecessarily

  • Maintenance problems

  • Conflicts in variable and function names

Based on the requirements of a project, there are also “frameworks” and “libraries” of packaged code and modules, which include utility functions and objects that can be used to save time. Most are free and supported by communities, such as the Prototype framework (www.prototypejs.org) or the jQuery library (http://jquery.com). Some, such as Dojo (http://dojotoolkit.org), have sponsorships from a number of big companies, yet are still free to use.

A popular framework can be of value because it’s very likely to have a higher level of quality due to the number of users who have contributed and who have been debugging. However, there are issues to consider:

  • Does the framework include code and features you don’t need? Is using a framework like using a sledgehammer on a thumbtack?

  • Is downloading a large library of code to the browser on every page a worthwhile expense, performance-wise?

  • Is the development path of the framework active and evolving?

  • Is there an active community that can help troubleshoot problems?

  • Does the framework make any unexpected modifications to the core JavaScript objects that might conflict with other code?

  • Does the framework adhere to best practices?

  • Will the team be able to agree or set policy on a framework, and not have some members using one framework and others going off on their own? There is a cost associated with inconsistency.

Whatever decision is made, particularly in a diverse organization with a large Web presence, the choice should be weighed carefully and implemented in a controlled and consistent manner. The whole issue is essentially a build vs. buy (or, in this case, use someone else’s) consideration, just like any other software components to be included in a project.

Example of jQuery Framework Code

For the purposes of this discussion, it makes sense to demonstrate the power available in using a JavaScript framework. It can save time, enforce a level of consistency across an organization, and eliminate browser-related bugs. It can also be a timesaver when performing common DOM operations against code.

First, let’s look at an example of a fairly modern, unobtrusive page with some scripts attached (FIGURE 3.7). Here’s the HTML:

<LINELENGTH>90</LINELENGTH>
<ul id="test-one">
   <li><a href="one.html">Item One</a></li>
   <li><a href="two.html">Item Two</a></li>
   <li><a href="frameworks.html">Frameworks</a></li>
   <li><a href="four.html">Item Four</a></li>
   <li><a href="five.html">Item Five</a></li>
</ul>

<p class="event-class">Testing Events</p>

<table id="test-two">
<tr>
   <td>Cell One</td>
   <td>Cell Two</td>
   <td>Cell Three</td>
</tr>
<tr>
   <td>Cell Four</td>
   <td>Cell Five</td>
   <td>Cell Six</td>
</tr>
<tr>
   <td>Cell Seven</td>
   <td>Cell Eight</td>
   <td>Cell Nine</td>
</tr>
<tr>
   <td>Cell Ten</td>
   <td>Cell Eleven</td>
   <td>Cell Twelve</td>
</tr>
</table>
A JavaScript page without a framework.

Figure 3.7. A JavaScript page without a framework.

Now let’s add some scripts, including an onload handler, a dynamically added event handler, and some solid getElementsByClassName() action for good measure. The following script does a few small things:

  1. First, it finds links in a list and compares them against the current address to highlight the “current” page link.

  2. Second, it attaches a click event to a paragraph with a certain class attached.

  3. Third, it takes a specific table and zebra-stripes it; that is, it makes every other row a different color by adding a class to each one.

Looking over the code, it is nice and unobtrusive and fairly well structured for the demonstration purposes:

<LINELENGTH>90</LINELENGTH>
// get elements by class name (low fidelity)
function getElementsByClassNameLF(tagName,aClass){
   var z = [];
   if (!document.getElementsByTagName) return z;
   var x = document.getElementsByTagName(tagName);
   for (var i = 0; i < x.length; i++){
      if (x[i].className == aClass) z.push(x[i]);
   }
   return z;
}

// first/last child
function findLink(id) {
   var els = document.getElementById(id).getElementsByTagName("a");
   var i = 0;
   while (i < els.length){
      if (document.location.href.indexOf(els[i].href) != -1){
         els[i].className += " on";
      }
      i++;
   }
}

// zebra stripes (every other)
function makeStripes(els){
   for (var i=els.length-1; i>=0; i--){
      if (i % 2 == 0){
         els[i].className += " even";
      }
   }
}

window.onload = function(){
   var tab = document.getElementById("test-two");
   var x = tab.getElementsByTagName("tr");
   makeStripes(x);
   findLink("test-one");
   var ev = getElementsByClassNameLF("p","event-class");
      ev[0].onclick = function(){
         alert("event added");
      }
}

It would, however, take some time to put the code together and verify that it is all working as intended, as it includes loops and arrays and so forth.

Now, the same document’s JavaScript code can be rewritten using the jQuery library framework (FIGURE 3.8), which has a pleasantly succinct syntax that’s easy to pick up by coders and noncoders alike. One appeal of the jQuery framework is its reliance on CSS selectors to locate DOM nodes in the document, which typically requires a complex set of operations using the W3C DOM code. Note how much simpler the above code is, refactored using jQuery:

<LINELENGTH>90</LINELENGTH>
$(document).ready(function(){
  $('#test-two tr:even').addClass('even'),

  $('#test-one a').each(function(){
     if (document.location.href.indexOf(this.href) != -1) {
        $(this).addClass('on'),
      }
   });

   $('p.event-class').click(function(){
      alert('event added'),
   });
})
The same page as illustrated in Figure 3.7, now using the jQuery framework—looks the same on the front end, but the performance is significantly improved and the amount of code is significantly decreased.

Figure 3.8. The same page as illustrated in Figure 3.7, now using the jQuery framework—looks the same on the front end, but the performance is significantly improved and the amount of code is significantly decreased.

It doesn’t take a JavaScript expert to notice how much more concise this code is, compared to the dozens of lines of code in the other version. It can take four long functions and distill them down to three easy statements.

What makes it so brief? The code uses the jQuery functions and syntax, which abstracts the heavy lifting. What’s not seen above is there is also a 20KB file downloaded with that brief script, whereas in the first example, all that was shown was all that was required.

But the code to do the work is much simpler: Where in the original the getElementsByClassName() function involved getting a reference to an array, looping through all the items, and applying a click with a function reference, the jQuery version simply does this:

<LINELENGTH>90</LINELENGTH>
$('p.event-class').click(function(){
   alert('event added'),
});

The power is in the exceptional $() function that takes all sorts of different selector references, such as #test-two tr:even, to retrieve every other row of a table. There is a reference online (http://docs.jquery.com) that shows all the different types of selectors that jQuery accepts. It supports CSS 1-3, XPath style selectors, and a number of custom ones.

Frameworks Make Ajax Easy

One benefit many are finding with JavaScript frameworks is that most of them make creation of Ajax code stunningly easy. For instance, using Ajax with jQuery is quite simple.

Imagine a simple HTML document that links to the jQuery library (FIGURE 3.9):

<LINELENGTH>90</LINELENGTH>
<h1>Frameworks - With Ajax</h1>

<p>
This contains
<a href="js-ajax-call.html">a link</a>
which will call a page from remote and load it into the page
without a refresh.
</p>
A sample document with a link that executes some Ajax using jQuery.

Figure 3.9. A sample document with a link that executes some Ajax using jQuery.

Now, link a simple script that unobtrusively adds a <div> element into which content can be loaded. The <div> is being added by the script because if the script did not run, the <div> would be meaningless.

<LINELENGTH>90</LINELENGTH>
$(document).ready(function(){
   $('body').append('<div id="ajax-div"></div>'),
})

Then, wire up an onclick for the link in the paragraph. The actual link’s href attribute will be used, so that if the script fails for some reason, the browser will still take and use the link value.

<LINELENGTH>90</LINELENGTH>
$(document).ready(function(){
   $('body').append('<div id="ajax-div"></div>'),
   $('p a').click(function(e){
      var x = $(this).attr('href'),
   });
})

Now, so far none of this is even technically Ajax, unless DOM manipulation and event handlers are added to the more broad definition. The actual Ajax in jQuery is the following line of code:

<LINELENGTH>90</LINELENGTH>
$('#ajax-div').load(x);

Which says, simply, load x into the <div> with the ID of #ajax-div (FIGURE 3.10). It doesn’t get much easier than that. So, the final script, with an additional line of code that prevents the user, who just clicked the link, from being sent to the other page, is:

<LINELENGTH>90</LINELENGTH>
$(document).ready(function(){
   $('body').append('<div id="ajax-div"></div>'),
   $('p a').click(function(e){
      var x = $(this).attr('href'),
      $('#ajax-div').load(x);
      e.preventDefault();
   });
})
A framework can make Ajax easy.

Figure 3.10. A framework can make Ajax easy.

jQuery also includes methods that perform HTTP get and post operations, complete with error handlers and callback functions. Frameworks can be real timesavers.

Frameworks in a Nutshell

The bottom line is that the decision to use a framework should be a reasoned and thought-out decision, just like any other software choice. Frameworks are written by people, and despite the large numbers of testers and contributors, bugs do surface and they are difficult to troubleshoot due to the complex nature of their code bases. There are frequently workarounds, however, due to the vast number of ways the same tasks can be accomplished with these powerful tools. There are pros and cons, and sometimes the con of a large code base for a few simple effects is just not worth the extra bandwidth and downloads. Again, it is best to make a reasoned decision.

It’s obvious that modern JavaScript programming has come a long way from the simple tricks and copy-paste scripts of the 1990s. Today’s Web sites are being built with a keen attention to separation of content from presentation, and this separation is hobbled if the behavior layer of JavaScript is intermingled with the other front-end code. Scripts are becoming more mature, including Ajax and W3C DOM-based code as opposed to proprietary browser-specific code. Code can be isolated and reused in external files and even cleaned up using an independently tested and constantly evolving JavaScript framework.

In the end, however, a Web team should weigh the advantages of a scripting framework against its own site’s and team’s needs. This will determine the best approach—the one the team should stick with to avoid the inconsistencies of days past.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.15.244.205