reptile7's JavaScript blog
Tuesday, October 25, 2011
 
The Banner Loop
Blog Entry #230

In today's post we will take up HTML Goodies' "Accessible JavaScript 101: Rotating Banners" tutorial, which is credited to an HTML Goodies "Forum Member Web Developer" (hereafter "FMWD"). The "Rotating Banners" tutorial offers code that, when pieced together correctly and paired with a suitable set of images, gives the following display:

Affiliate Links

  • Search Web with Google
  • Search Web with Yahoo!
  • Search Web with MSN
 
Three image-link banners - one for Google, one for Yahoo!, one for MSN - display one at a time in an endless loop. Clicking on a banner will take you to the Web site of the company named on the banner. The tutorial code's JavaScript employs a custom JavaScript object and a variety of Core DOM properties to create the rotating banner display, and is thus quite a bit different from the image-cycle animation script presented in Blog Entry #147. HTML/CSS The tutorial code's HTML structures the banners to be rotated as an unordered list preceded by an (optional) Affiliate Links header and wrapped in an id="banners" div container: <div id="banners"> <h4>Affiliate Links</h4> <ul> <li><a href="www.google.com"><img alt="Search Web with Google" src="googlelogo.png"></a></li> <li><a href="www.yahoo.com"><img alt="Search Web with Yahoo" src="yahoologo.png"></a></li> <li><a href="www.msn.com"><img alt="Search Web with MSN" src="msnlogo.png"></a></li> </ul> </div> • The ul/li scaffolding is not really necessary but makes semantic sense if we want all of the banners to be visible for users without JavaScript support. • The anchor href values are missing their http:// parts - don't forget to pencil them in if you make use of this code. • FMWD doesn't provide a demo or any images to work with; for my demo above, the Google logo was picked out at this page, the Yahoo! logo was picked out at this page, and the MSN logo was picked out at this page. The tutorial code's CSS styles the banners div and its descendants in various ways; most importantly, it places the banners div in the page's upper-right-hand corner via: #banners { position: absolute; top: 10px; right: 10px; } Supposing that each banner image is 200px by 80px, FMWD gives the unordered list a #banners ul { display: block; height: 80px; overflow: hidden; } style rule set that effectively hides the Yahoo! and MSN banners when the page loads. However, we will later zero out those banners with liObject.style.display = "none"; commands and thus the height:80px; and overflow:hidden; declarations are redundant; moreover, ul elements are block-level elements (unless you set them otherwise), so we don't need the display:block; declaration either. FMWD notes that alternatively setting overflow to auto will allow users without JavaScript support to access the Yahoo! and MSN banners via a vertical scrollbar (overflow:scroll; will also do this); my own preference would be to throw out the overflow declaration and display all three banners simultaneously. FMWD also applies display:block; to the list's li children - li elements are also block-level elements - and attempts to get rid of the list's item markers via a list-item-style:none; declaration: #banners li { display: block; list-item-style: none; } In fact, CSS doesn't have a list-item-style property: list-style-type:none; or list-style:none; is what we want. Interestingly, the preceding rule set does remove the li markers, but it's the display:block; declaration that does the removing; presumably this is because the li elements must have an effective display value of list-item for the markers to be present. Rounding out the CSS are rules that subtract the Affiliate Links header, any link-induced img borders, and margin and padding areas within the unordered list: #banners h4 { display: none; } #banners img { border: none; } /* Firefox users will see blue img borders without this rule. */ #banners ul, #banners li, #banners a, #banners img { margin: 0; padding: 0; } If your images are not actually 200px by 80px but merely have a ≅200/80 width/height ratio, as is the case for my demo images, then you will want to add width:200px; and height:80px; declarations to the #banners img rule set. JavaScript Here's the display so far if we give the unordered list a visible overflow (its default setting):

Affiliate Links

  • Search Web with Google
  • Search Web with Yahoo!
  • Search Web with MSN
 
The list's li children do not have ids, so let's call them listItems[0], listItems[1], and listItems[2]. (You'd think that the W3C would have equipped the DOM HTMLUListElement and HTMLOListElement interfaces with a listItems attribute, but it didn't.) The tutorial code's JavaScript displays one list item and zeroes out the other two in each frame of the rotating banner animation: (1) listItems[0] is switched on and listItems[1] and listItems[2] are switched off in the first frame; (2) listItems[0] is switched off, listItems[1] is switched on, and listItems[2] is switched off in the second frame; (3) listItems[0] and listItems[1] are switched off and listItems[2] is switched on in the third frame; and so on for subsequent frames. The script does not change the source order of the li elements; the Yahoo! and MSN banners appear in the listItems[0] position because the li elements that precede them are removed from the formatting structure entirely (quoting the W3C) via a none display setting. At the beginning of the tutorial's Advanced Functionality Layer - JavaScript section, FMWD exhorts the reader to encapsulate the script operations within a class and then lays out the following template therefor: function lIterator(listElement, delay) {     /* Constructor */     /* Dynamic Methods */ } /* Static Properties */ /* Static Methods */ Some people come to JavaScript from a simpler direction, namely, they begin their coding education by learning HTML, they pick up some CSS, and then they move on to JavaScript. Some people come to JavaScript from a more advanced direction, that is, from a programming language such as C or Java. I'm in the former group; FMWD would certainly seem to be in the latter group. JavaScript does not use the class data structure and it does not have dynamic methods (or at least it doesn't call them that). The terms static property and static method do crop up in the Mozilla JavaScript Reference (e.g., parse( ) is a static method of the Date object) but do not apply in the present context. But at least the constructor part is right: the lIterator( ) function will be used to construct a custom JavaScript object that carries out the banner display action. I don't like the lIterator name because a lowercase l and an uppercase I look the same in a sans-serif font - Mozilla wouldn't like it either because it begins with a lowercase letter - and I will call the constructor function Rotator( ) going forward. The custom object is created ("instantiated") via an onload event handler: <body onload="new Rotator((document.getElementById('banners')).getElementsByTagName('ul')[0], 1000);"> <!-- The outer parentheses surrounding (document.getElementById('banners')) are unnecessary. --> When the document loads, Rotator( ) is called and passed its call arguments à la a normal function. The first Rotator( ) call argument, document.getElementById('banners').getElementsByTagName('ul')[0], references the unordered list (assuming that the list is the first-in-source-order ul element in the document) and is given a listElement identifier. The second Rotator( ) call argument, 1000, will be used to set the animation frame rate and is given a delay identifier. We're ready to fill in the Rotator( ) function body. FMWD's Rotator( ) code deals with the unordered list and its contents on a node-by-node basis (vis-à-vis, say, an element-by-element basis), accordingly beginning with a for loop that strips the list of any child nodes that are not li elements: function Rotator(listElement, delay) {     for (var i = listElement.childNodes.length - 1; i >= 0; i--)         if (!/li/i.test(listElement.childNodes[i].nodeName))             listElement.removeChild(listElement.childNodes[i]); As written out in the previous section, the unordered list has seven child nodes: (2,4,6) The three li elements are the second, fourth, and sixth child nodes of the list; the nodeName of these Element nodes is LI. (1,3,5,7) The end-of-line characters surrounding the li elements are the first, third, fifth, and seventh child nodes of the list; the nodeName of these Text nodes is #text. In each loop iteration, an if statement test( )s for a match between (a) the nodeName of a given listElement child node and (b) a /li/i* regular expression literal; if the match is unsuccessful (note the ! operator preceding the literal), as is the case for the end-of-line Text nodes, then the node is removed via a removeChild( ) command. *There's actually no need to case-insensitize the regexp pattern. We can and should use a LI pattern and ditch the i flag: the W3C stipulates that for an Element node in an HTML document, nodeName (Element.tagName) must give an all-uppercase return. FMWD correctly points out that a comment node <ul> <!-- The banners are coded below. --> <li>... whose nodeName would be #comment, would also be removed by the loop. The childNodes attribute, the nodeName attribute, and the removeChild( ) method are all part of the DOM Node interface. Also check out Mozilla's Node interface portal and its links to pages that flesh out the various Node attributes and methods. The preceding loop is followed by another loop that reduces the listItems[1] and listItems[2] li elements (the ones holding the Yahoo! and MSN banners) to nothingness: for (var i = 1; i < listElement.childNodes.length; i++)     listElement.childNodes[i].style.display = "none"; (In an earlier version of the tutorial the < character in the for condition was not escaped and for (var i = 1; i was all that displayed - this is what djt is talking about in the tutorial comment thread - the code is OK now.) FYI: listElement.childNodes[i].style would throw an error with an end-of-line Text node or a comment node; as far as I am aware, styles cannot be applied to non-Element nodes. Neither of the above loops directly relates to the state or behavior of the custom Rotator( ) object; their operations could be executed outside of the Rotator( ) function if desired. However, the remainder of FMWD's Rotator( ) code does pertain to the Rotator( ) object and we'll go through it in detail in the next post. reptile7

Comments: Post a Comment

<< Home

Powered by Blogger

Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)