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
elementIDas anobject. getElementsByTagName('tag')- returns all elements with the name
tagas 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
firstChildandlastChild, 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
H2headline 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
hrefattribute does not containwindow.location.hostname) and add thehrefof the link as a text in parenthesis after the link. Until you learn how to do that propely, use theinnerHTMLattribute of the linkobjectto change its content. Solution external links. - Add an
onclickhandler to each link with the attributetargetthat opens a 400x400 pixels pop-up window. Solution pop-up.
