Article

Home » Client-side Coding » JavaScript & Ajax Tutorials » Enhancing Structural Markup with JavaScript

About the Author

Simon Willison

author_simonwillison Simon is a seasoned Web developer from the UK, currently working in Lawrence, Kansas. He specializes in both client- and server-side development, and recently became a member of the Web Standards project. Visit him at http://simon.incutio.com/, and at Stylish Scripting: SitePoint's DHTML and CSS Blog.

View all articles by Simon Willison...

Enhancing Structural Markup with JavaScript

By Simon Willison

December 10th, 2003

Reader Rating: 7.5

Page: 1 2 3 Next

Just a few years ago, the key skill involved in writing HTML was knowing enough table tricks to persuade the two major browsers to do more or less what you wanted them to. The modern Web is a very different beast, where the quality of your markup is judged on the basis of how well you use structural elements such as headers, paragraphs and lists to describe your content.

The benefits of this approach have been explained many times before: more maintainable code, smaller file sizes, better accessibility and the ability to control your site's look and feel from a single style sheet rather than hacking away at sprawling chunks of markup spread across multiple pages. An advantage that is not so frequently discussed is that well structured markup opens the door for additional site enhancements based around that long-abused third leg of the client-side Web, Javascript.

This article will look at two ways in which Javascript and well structured markup can work together. The first example will show how Javascript can enhance a blockquote by hooking into its cite attribute. The second will demonstrate a "best of breed" script for building links that toggle which panel is visible on a page.

Blockquote Citations

For our first example, let's take a look at the humble blockquote element. Often misused to apply indentation, the correct usage of this element is to mark up quotations that should appear visually separate from surrounding text. The opening blockquote tag can take an optional cite attribute, which should contain the URL of the page on which the quotation originated.

The only problem with the cite attribute is that browsers completely ignore it. Markup purists may appreciate it, but, from a purely practical point of view, nothing is gained by using it save a feeling of smug satisfaction at using the correct markup. This is where Javascript comes in. Using the DOM, it is possible to add a link to the quotation source at the bottom of any blockquote that has a cite attribute. Here's the code for a function that does just that:

function extractBlockquoteCitations() {
 var quotes = document.getElementsByTagName('blockquote');
 for (var i = 0; i < quotes.length; i++) {
   var cite = quotes[i].getAttribute('cite');
   if (cite != '') {
     var a = document.createElement('a');
     a.setAttribute('href', cite);
     a.setAttribute('title', cite);
     a.appendChild(document.createTextNode('Source'));
     var p = document.createElement('p');
     p.className = 'blockquotesource';
     p.appendChild(a);
     quotes[i].appendChild(p);
   }
 }
}

Let's take a close look at the body of the function.

var quotes = document.getElementsByTagName('blockquote');

This line uses the DOM method, getElementsByTagName, to find all of the blockquote elements in the current page, assigning them to an array (it's actually an HTMLCollection, but conveniently this is a data structure that behaves like an array) called quotes.

for (var i = 0; i < quotes.length; i++) {
 var cite = quotes[i].getAttribute('cite');
 if (cite != '') {

Now we're looping through the gathered blockquote nodes. Each time, we use the getAttribute method to retrieve the cite attribute from the element. If the cite attribute has been set, we go on to the fun part: creating a "Source" link at the bottom of the quotation.

   var a = document.createElement('a');
   a.setAttribute('href', cite);
   a.setAttribute('title', cite);

When we want dynamically to add new HTML elements to a page using the DOM, the correct way of doing so is to create the elements programmatically using the createElement method. The above lines create a new 'a' element and assign it href and title attributes, both set to the URL of the citation.
a.appendChild(document.createTextNode('Source'));

We want the link element to contain some text that the user can click on to activate the link. Raw text nodes are created using the createTextNode method. The DOM treats HTML elements as forming a tree, so to add the text to our newly created link we need to call its appendChild method.

   var p = document.createElement('p');
   p.className = 'blockquotesource';
   p.appendChild(a);

To allow us to flexibly style the new link using CSS, we can wrap it in a paragraph element. The above code creates such an element, sets its class to 'blockquotesource' to provide a hook for our CSS, then adds the link to it using appendChild. At this point, the new document fragment we have constructed is equivalent to the following HTML:

<p class="blockquotesource">
<a href="[cite URL]" title="[cite URL]">Source</a>
</p>

At the moment, out fragment remains invisible because, while we have created it in memory, we have not yet attached it to our document. The last line in the function does just that:

   quotes[i].appendChild(p);

quotes[i] is the blockquote element we're currently processing. appendChild attaches our new paragraph to the blockquote, making it visible in the process.

There are two more steps. Firstly, we need the above function to run when the page is first loaded. There are a number of ways of achieving this. The most simple is to add a call to the function to the onload attribute of the document's body element:

<body onload="extractBlockquoteCitations();">

This works just fine, but we can do better. Since our Javascript function will be hosted in an external file, wouldn't it make sense for the external file to cause the function to run as well? The naive way of doing that is with the following line of Javascript:

window.onload = extractBlockquoteCitations;

Note that we have provided the name of the function, but neglected the () at the end, which would cause the function to execute. Javascript supports the functional programming style, which means that functions can be treated just like any other data object and passed around as arguments, stored in data structures, or even returned from other functions. I will talk about this topic more in a future article, but the upshot of it is that assigning a function to window.onload will cause it to execute once the page has finished loading.

This solution, however, also has a drawback. If you want to use on a given page multiple scripts that execute when the page has finished loading, the last script to register itself with window.onload will be the only script to execute. What's really needed is a way to attach our function to the onload handler of the window object without overwriting what's already there. Unfortunately, Internet Explorer and other browsers differ on how this kind of dynamic event attachment should be handled; fortunately, Scott Andrew has released a function that handles these differences for you. Here's the function:

function addEvent(obj, evType, fn){
 if (obj.addEventListener){
   obj.addEventListener(evType, fn, false);
   return true;
 } else if (obj.attachEvent){
   var r = obj.attachEvent("on"+evType, fn);
   return r;
 } else {
   return false;
 }
}

And here's the code to add our blockquotes function to the load event of the window object:

addEvent(window, 'load', extractBlockquoteCitations);

The final step is to style our quotations using CSS. Here's a relatively simple CSS snippet for handling blockquotes:

blockquote {
 border-left: 0.25em solid navy;  
 padding: 0 0.5em;  
 margin: 0.5em 1.5em 0.5em 2.5em;  
}
blockquote p.blockquotesource {
 font-weight: bold;
 font-size: 0.8em;
 text-align: right;
 padding-top: 0.5em;
}

The finished product can be viewed here.

If you liked this article, share the love:
Print-Friendly Version Suggest an Article

Sponsored Links