Up until now, I’ve avoided special cases for any specific browsers. As long as you are using any DOM-compliant browser, all of the scripts you’ve seen so far will work. Unfortunately, the
displayAbbreviations function is the exception to the rule.
The displayAbbreviations function works fine—unless you try it in version 6 or earlier of Internet Explorer for Windows. If you load explanation.html in Internet Explorer, you won’t see a list of abbreviations, and you will probably get a JavaScript error message.
This seems like very strange behavior. After all, the function begins with some object detection to ensure that only DOM-capable browsers will execute the code. Internet Explorer is clearly capable of understanding methods like getElementsByTagName and getElementById.
The problem goes back to the browser wars described in Chapter 1. The abbr and acronym elements were used as weapons by Netscape and Microsoft. In the heat of battle, Microsoft decided not to implement the abbr element.
The browser wars are long over. Microsoft won its battle against Netscape, but it didn’t get around to implementing a proper abbr element until Internet Explorer 7. The displayAbbreviations function fails in earlier versions because it attempts to extract attribute and text nodes from abbr element nodes, and Internet Explorer refuses to accord these abbr nodes the status of being elements.
We seem to have unwittingly stepped on an unexploded bomb left over from a long-finished battle.
There are three possible solutions:
• Use acronym elements instead of abbr elements. This option doesn’t appeal to me, because it means sacrificing semantically correct markup just to satisfy a stubborn browser.
• Use an html namespace on the element (<html:abbr>abbr</html:abbr>), which would allow Internet Explorer to recognize the element. This involves modifying the markup and really doesn't solve the problem if the displayAbbreviations function is used in a different document.
• Ensure that displayAbbreviations degrades gracefully in Internet Explorer. This is quite easy to accomplish and is the most appropriate. By adding just a couple of extra lines, Internet Explorer (or any other browser that doesn’t want to recognize the abbr element) will simply exit the function early.
So, let’s go with the third option.
First, add a single line to the loop that gathers the title attributes and text values from the abbr elements:
for (var i=0; i<abbreviations.length; i++) { var current_abbr = abbreviations[i];
if (current_abbr.childNodes.length < 1) continue;
var definition = current_abbr.getAttribute("title");
var key = current_abbr.lastChild.nodeValue;
defs[key] = definition;
}
This is effectively saying, “If the current element has no child nodes, carry on to the next iteration of the loop.” Internet Explorer will count up the child nodes for each abbr element and incorrectly come up with zero each time. This new statement ensures that it doesn’t attempt to go any further in the loop.
When Internet Explorer gets to the loop in displayAbbreviations that builds the definition list, it won’t create any dt or dd elements because the defs array is empty. Let’s add one line after that loop. If the definition list has no child nodes, exit the function:
// create the definition list
var dlist = document.createElement("dl");
// loop through the definitions for (key in defs) {
var definition = defs[key];
// create the definition title
var dtitle = document.createElement("dt");
var dtitle_text = document.createTextNode(key);
dtitle.appendChild(dtitle_text);
// create the definition description var ddesc = document.createElement("dd");
var ddesc_text = document.createTextNode(definition);
ddesc.appendChild(ddesc_text);
// add them to the definition list dlist.appendChild(dtitle);
dlist.appendChild(ddesc);
}
if (dlist.childNodes.length < 1) return false;
Again, this runs counter to the principles of structured programming because there is now an additional exit point in the middle of the function. But this is probably the simplest way of dealing with Internet Explorer’s quirk without altering the existing function significantly.
The finished function looks like this:
function displayAbbreviations() {
if (!document.getElementsByTagName || !document.createElement
➥|| !document.createTextNode) return false;
// get all the abbreviations
var abbreviations = document.getElementsByTagName("abbr");
if (abbreviations.length < 1) return false;
var defs = new Array();
// loop through the abbreviations
for (var i=0; i<abbreviations.length; i++) { var current_abbr = abbreviations[i];
if (current_abbr.childNodes.length < 1) continue;
var definition = current_abbr.getAttribute("title");
var key = current_abbr.lastChild.nodeValue;
defs[key] = definition;
}
// create the definition list
var dlist = document.createElement("dl");
// loop through the definitions for (key in defs) {
var definition = defs[key];
// create the definition title
var dtitle = document.createElement("dt");
var dtitle_text = document.createTextNode(key);
dtitle.appendChild(dtitle_text);
// create the definition description
var ddesc = document.createElement("dd");
var ddesc_text = document.createTextNode(definition);
ddesc.appendChild(ddesc_text);
// add them to the definition list dlist.appendChild(dtitle);
dlist.appendChild(ddesc);
} if (dlist.childNodes.length < 1) return false;
// create a headline
var header = document.createElement("h2");
var header_text = document.createTextNode("Abbreviations");
header.appendChild(header_text);
// add the headline to the body document.body.appendChild(header);
// add the definition list to the body document.body.appendChild(dlist);
}
The two new lines will ensure that there will be no errors even if the abbr element isn’t understood.
They act as safety checks, much like the object detection at the start of the script.
■ Note Notice that even though the problem was caused by a specific browser, there’s still no need to use browser-sniffing code. Sniffing for a specific browser name and number is bound to cause problems and result in very convoluted code.
We have successfully defused a nasty surprise left over from the legacy of the browser wars. If nothing else, it serves as a reminder of the importance of standards. Because their browser doesn’t support the abbr element, users of Internet Explorer won’t get the benefit of seeing a generated list of abbreviations. But they will still be able to view the core content. The definition list of abbreviations provides a nice enhancement, but it is by no means a vital part of the page. If it were, it would have been included in the markup to begin with.