27.6. DOM Scripting in ActionTo better appreciate the power of DOM Scripting, take a look at two relatively simple examples . Instead of breaking down the JavaScript into chunks, helpful JavaScript comments have been added that discuss what the script is doing at each step in the process. The reasoning for this is twofold: it lets you see the script as a whole and it gets you used to seeing comments in code (a habit which is worth picking up to ease long-term maintenance). 27.6.1. Example 1: Style Sheet SwitcherThis function is an extreme simplification of the technique employed in Invasion of the Body Switchers (alistapart.com/articles/bodyswitchers) by Andy Clarke and James Edwards. It also makes use of John Resig's addEvent( ) function (quirksmode.org/blog/archives/2005/10/_and_the_winner_1.html) for simple event management. It uses a single external CSS file to handle three different font sizes for the browser. We are going to change the font size of the page by changing a class on the body element. We start with the external style sheet (switcher.css): body.normal { font-size: 80%; } body.large { font-size: 100%; } body.huge { font-size: 150%; } h1 { font-size: 2em; } p { font-size: 1em; } Then we have our XHTML page, with a link to our CSS file, and a placeholder script referring to our JavaScript file (switcher.js): <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>Switcher Example</title> <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" /> <meta http-equiv="Content-Language" content="en-us" /> <link rel="stylesheet" type="text/css" media="screen" href="switcher.css" /> <script type="text/javascript" src="/books/4/439/1/html/2/switcher.js"></script> </head> <body > <div > <h1>Title</h1> <p>This is a paragraph.</p> <p>This is another paragraph.</p> </div> <div> <form action="switch.php"> <fieldset> <legend>Please choose a font size</legend> <select name="size"> <option value="normal" selected="selected">Normal</option> <option value="large">Large</option> <option value="huge">Huge</option> </select> <input type="submit" value="Apply It" /> </fieldset> </form> </div> </body> </html> Before we get to the JavaScript, let's take a quick look at the XHTML being used. Notice that the switcher form has an action (switcher.php). Even though we will be taking control of this form with JavaScript, we've provided a server-side alternative for non-JavaScript-enabled browsers. This is a prime example of graceful degradation. All right, now on to the JavaScript. As this script will have only one use per page, I am going to create it as an object literal (see Chapter 26): var switcher = { body: false, // the body element of the page form: false, // the switcher form controller: false, // the controlling form element init: function( ){ // the initialization function /* check for method availability, return if used methods are unsupported or id-ed elements used are not available */ if( !document.getElementById || !document.getElementsByTagName || !document.getElementById( 'switcher_form' ) ) return; // assign the body switcher.body = document.getElementsByTagName( 'body' )[0]; // assign the form switcher.form = document.getElementById( 'switcher_form' ); // assign the select element to the controller switcher.controller = switcher.form.getElementsByTagName( 'select' )[0]; // add an event switcher.addEvent( switcher.controller, // to the controller 'change', /* trigger with the onchange event */ function( ){ // run this function /* set the body's class equal to the value of the controller */ switcher.body.className = this.value; } ); // get the submit button var input = switcher.form.getElementsByTagName( 'input' )[0]; // delete it as our onchange event has made it redundant input.parentNode.removeChild( input ); }, addEvent: function( obj, type, fn ){ // the add event function if (obj.addEventListener) obj.addEventListener( type, fn, false ); else if (obj.attachEvent) { obj["e"+type+fn] = fn; obj[type+fn] = function( ) { obj["e"+type+fn]( window.event ); }; obj.attachEvent( "on"+type, obj[type+fn] ); } } }; /* using the object's built-in addEvent function, trigger this object's init( ) method on page load */ switcher.addEvent( window, 'load', switcher.init ); 27.6.2. Example 2: Page GlossaryThis script is pageGlossary from Easy Designs (easy-designs.net/code/pageGlossary/). It traverses a specified portion of the document (identified by an id), collecting all of the abbreviation, acronym, and definition elements, and writes them out to the designated portion of the page (also identified by an id) as a formal page glossary. The script also removes duplicate entries and sorts the contents alphabetically. Here it is: var pageGlossary = { getFrom: false, // where to collect terms from buildIn: false, // where to place the glossary glossArr: [], // the working glossary as an array usedArr: [], // terms we've used ( to track duplicates ) init: function( fromId, toId ){ /* init( ) takes two arguments: * id of the collection area * id of the destination */ // make sure the required methods and elements are available if( !document.getElementById || !document.getElementsByTagName || !document.getElementById( fromId ) || !document.getElementById( toId ) ) return; // set the collection area pageGlossary.getFrom = document.getElementById( fromId ); // set the destination area pageGlossary.buildIn = document.getElementById( toId ); // run the collection method (below) pageGlossary.collect( ); // if the glossary array has no members, quit now if( pageGlossary.usedArr.length < 1 ) return; // resort the array in alphabetical order using ksort (below) pageGlossary.glossArr = pageGlossary.ksort( pageGlossary.glossArr ); // run the build method (below) pageGlossary.build( ); }, collect: function( ){ // the collection method /* get all the abbr, acronym and dfn elements inside the collection area */ var dfns = pageGlossary.getFrom.getElementsByTagName('dfn'); var abbrs = pageGlossary.getFrom.getElementsByTagName('abbr'); var acros = pageGlossary.getFrom.getElementsByTagName('acronym'); var arr = []; // a temp array to hold the collections // populate the temp array arr = arr.concat( dfns, abbrs, acros ); // quit if nothing was collected if( ( arr[0].length == 0 ) && ( arr[1].length == 0 ) && ( arr[2].length == 0 ) ) return; // save processing time by storing the length of the raw array var arrLength = arr.length; // interate through the raw array for( var i=0; i < arrLength; i++ ){ // store the nested array length var nestedLength = arr[i].length; // skip this array if it has no members if( nestedLength < 1 ) continue; // iterate through the array for( var j=0; j < nestedLength; j++ ){ // make sure the element has some children if( !arr[i][j].hasChildNodes( ) ) continue; // collect the term var trm = arr[i][j].firstChild.nodeValue; // collect the definition var dfn = arr[i][j].getAttribute( 'title' ); // if this term is not in the used array if( !pageGlossary.inArray( trm, pageGlossary.usedArr ) ){ // push it to the used array pageGlossary.usedArr.push( trm ); /* and store its definition in the glossary array, using the term as its key value */ pageGlossary.glossArr[trm] = dfn; } } } }, build: function( ){ // the builder method // create a level heading var h2 = document.createElement('h2'); // have it read "Page Glossary" h2.appendChild( document.createTextNode( 'Page Glossary' ) ); // create the definition list var dl = document.createElement('dl'); // give it a class of pageGlossary dl.className = 'pageGlossary'; // iterate through the glossary array for( key in pageGlossary.glossArr ){ // create a definition term element var dt = document.createElement( 'dt' ); // make its text the term dt.appendChild( document.createTextNode( key ) ); // append it to the list dl.appendChild( dt ); // create the definition data element var dd = document.createElement('dd'); // make its text the definition dd.appendChild( document.createTextNode( pageGlossary.glossArr[key] ) ); // append it to the list dl.appendChild( dd ); } // append the h2 to the target element pageGlossary.buildIn.appendChild( h2 ); // append the dl to the target element pageGlossary.buildIn.appendChild( dl ); }, addEvent: function( obj, type, fn ){ // the add event function if (obj.addEventListener) obj.addEventListener( type, fn, false ); else if (obj.attachEvent) { obj["e"+type+fn] = fn; obj[type+fn] = function( ) { obj["e"+type+fn]( window.event ); }; obj.attachEvent( "on"+type, obj[type+fn] ); } }, ksort: function( arr ){ // the key sorting function var rArr = [], tArr = [], n=0, i=0, el; for( el in arr ) tArr[n++] = el + '|' + arr[el]; tArr = tArr.sort( ); var arrLength = tArr.length; for( var i=0; i < arrLength; i++ ){ var x = tArr[i].split( '|' ); rArr[x[0]] = x[1]; } return rArr; }, inArray: function( n, h ){ // the inArray test var l = h.length; for( var i=0; i < l; i++ ){ if( h[i] === n ) return true; } return false; } }; // add pageGlossary.init( ) method to the page's onload event pageGlossary.addEvent( window, 'load', function( ){ pageGlossary.init( 'content', 'extras' ); } ); |