
How to reach what we want to change
For the inexperienced Javascript developers, HTML is their playground.
HTML: <a href="index.html" onmouseover="image1.src='1on.gif'" onmouseout="image1.src='1off.gif'"> <img src="1off.gif" name="image1" border="0" height="150" width="150" alt="home"></a>
or if they are a bit more advanced:
HTML: <a href="index.html" onmouseover="roll('home',1)" onmouseout="roll('home',0)"> <img src="home.gif" name="home" border="0" height="150" width="150" alt="home"></a> Javascript: // preloading image homeoff = new Image(); homeoff.src = 'home.gif'; homeon = new Image(); homeon.src = 'homeoff.gif'; function roll(imgName,a) { imgState=a==0?eval(imgName + 'on.src'):eval(imgName + 'off.src'); document.images[imgName].src = imgState; }
In any case, all the event calls are in the HTML, and if the function name changes, we'd have to change each document. Furthermore, each rollover means a lot of markup which adds to the overall page weight.
Out! Out! - you demons of inline event calls
Let's forget for the moment, that almost every rollover effect these days can be achieved via CSS rather than Javascript - let's say we want to use the following markup and create a rollover for the image:
HTML: <a href="index.html"><img src="home.gif" id="home" alt="home"></a>
Now, how to change that when the mouse is over it?
Climbing the branches of the node tree
Each XML (and that
includes HTML)
document is a node tree. A node is a part of this tree (think of a
file or a folder in windows explorer when you navigate around your harddisk). A
node can be
twelve different things[1] - for HTML,
only three are really interesting: element
, TextNode
and
AttributeNode
.
Our climbing equipment
Let's see which functions and attributes we can use to navigate the node tree of a document, and how to jump from one element to another.
Functions to reach an element in the page
getElementById('elementID')
- returns the element with the id
elementID
as anobject
. getElementsByTagName('tag')
- returns all elements with the name
tag
as anarray
.
Of course you can mix and match these two. Some examples:
document.getElementById('navigation').getElementsByTagName('a')[3]; returns the fourth link inside the element with the ID 'navigation' document.getElementsByTagName('div')[2].getElementsByTagName('p')[0]; returns the first paragraph inside the third div in the document.
Tools to navigate from a certain element
childNodes
- returns an array of all the nodes inside the current one. There is also
firstChild
andlastChild
, which are shorter versions forchildNodes[0]
andchildNodes[this.childNodes.length-1]
. parentNode
- The element containing this one
nextSibling
- the next element on the same level in the document tree
previousSibling
- the previous element on the same level in the document tree
All of these can be mixed at will and need.
Javascript: var other=document.getElementById('nav').childNodes[3].firstChild; returns the 4th element's first sub element inside the element with the ID nav. var prevlink=o.parentNode.previousSibling.firstChild.childnodes[2]; returns the third node inside the previous element that is on the same level as the parent element of o.
Attributes and functions for elements
attributes
- returns an array of all the attributes of this element. Does not work with Internet Explorer below version 6.
data
- returns or sets the textual data of the node
nodeName
- returns the name of the node (the HTML element name)
nodeType
- returns the type of the node — 1 is an element node, 2 attribute and 3 text.
nodeValue
- returns or sets the value of the node. This value is the text when the node is a textnode, the attribute if it is an attribute or null if it is an element.
getAttribute(attribute)
- returns the value of the attribute
attribute
.
Javascript: var other=document.getElementById('nav').firstChild; if(other.nodeType==3) { other.data='newtext'; } if(other.nodeType==1) { other.firstChild.data='newtext'; }
Now to reach the image in our example, we can use either
getElementsByTagName
or getElementById
.
HTML: <a href="index.html"><img src="home.gif" id="home" alt="home"></a> Javascript: function findimg() { var image; image=document.getElementById('home'); if (image) { image.style.border='3px dashed #ccc'; } } or: function findimg() { var imgs,i; imgs=document.getElementsByTagName('img'); for(i in imgs) { if(/home.gif/.test(imgs[i].src)) { imgs[i].style.border='3px dashed #ccc'; } } }
Using getElementById
is a lot easier, as we don't have to loop
through all elements and find a unique identifier. In this example, we checked
if the src attribute of the image object contains 'home.gif'. A more common
way is to check for a special class.
HTML: <a href="index.html"><img src="home.gif" class="roll" alt="home"></a> Javascript: function findimg() { var imgs,i; imgs=document.getElementsByTagName('img'); for(i in imgs) { if(/roll/.test(imgs[i].className)) { imgs[i].style.border='3px dashed #ccc'; } } }
Now, to add a roll-over effect, all we need to do is to add a function that does the switching of the image source, and adds the event handlers to the image.
function findimg() { var imgs,i; // loop through all images of the document imgs=document.getElementsByTagName('img'); for(i=0;i<imgs.length;i++) { // test if the class 'roll' exists if(/roll/.test(imgs[i].className)) { // add the function roll to the image onmouseover and onmouseout and send // the image itself as an object imgs[i].onmouseover=function(){roll(this);}; imgs[i].onmouseout=function(){roll(this);}; } } } function roll(o) { var src,ftype,newsrc; // get the src of the image, and find out the file extension src = o.src; ftype = src.substring(src.lastIndexOf('.'), src.length); // check if the src already has an _on and delete it, if that is the case if(/_on/.test(src)) { newsrc = src.replace('_on',''); }else{ // else, add the _on to the src newsrc = src.replace(ftype, '_on'+ftype); } o.src=newsrc; } window.onload=function(){ findimg(); } Test this example here.
Good for the moment, but we forgot one thing: Albeit being merely eye candy, a rollover should work without a mouse, too. To achieve this, we need to check if the link around the image gets the focus or not, as the image itself is not reachable via the keyboard when embedded in a link.
To do this, we need to get the element that contains the image, in this case
the link. We do this via parentNode
command. As this also changes
the object that gets sent as a parameter to the function roll()
,
we need to find the image again. Hence we loop through the childNodes of the
link and check which one is an element and an image by checking nodeType
and nodeName
. This is necessary, as some browsers see whitespace in
the source as an own node, whereas others don't.
function findimg() { var imgs,i; // Loop through all images, check if they contain the class roll imgs=document.getElementsByTagName('img'); for(i=0;i<imgs.length;i++) { if(/roll/.test(imgs[i].className)) { // add the function roll to the parent Element of the image imgs[i].parentNode.onmouseover=function(){roll(this);}; imgs[i].parentNode.onmouseout=function(){roll(this);}; imgs[i].parentNode.onfocus=function(){roll(this);}; imgs[i].parentNode.onblur=function(){roll(this);}; } } } function roll(o) { var i,isnode,src,ftype,newsrc,nownode; // loop through all childNodes for (i=0;i<o.childNodes.length;i++) { nownode=o.childNodes[i]; // if the node is an element and an IMG set the variable and exit the loop if(nownode.nodeType==1 && /img/i.test(nownode.nodeName)) { isnode=i; break; } } // check src and do the rollover src = o.childNodes[isnode].src; ftype = src.substring(src.lastIndexOf('.'), src.length); if(/_on/.test(src)) { newsrc = src.replace('_on',''); }else{ newsrc = src.replace(ftype, '_on'+ftype); } o.childNodes[isnode].src=newsrc; } window.onload=function(){ findimg(); } Test this mouse independent example here.
Why don't you try it?
Simply download the demo HTML and try one of the following tasks. Follow the solution links to see one possible solution. The solutions have the Javascript inline as a demonstration, not in an extra document where they should be. This was done to help you see the necessary markup in one document rather than two.
- Change the colour of every
H2
headline to blue. Solution colourchange. - Outline every second paragraph with a black border. Solution p-border.
- Check which of the links in the document is external
(by checking that the
href
attribute does not containwindow.location.hostname
) and add thehref
of the link as a text in parenthesis after the link. Until you learn how to do that propely, use theinnerHTML
attribute of the linkobject
to change its content. Solution external links. - Add an
onclick
handler to each link with the attributetarget
that opens a 400x400 pixels pop-up window. Solution pop-up.