Newsflash

 
Home arrow Buzzword-noncompliant arrow HTML and Javascript of wavy buttons
HTML and Javascript of wavy buttons
Written by Administrator   
Wednesday, 05 April 2006

In this article I explain how I created the visual effects for the front page of my site. Its main schtick is to pretend as if there's nothing there, just a starry background, and then unexpectedly pop up images and text when you move the mouse over that starry background.

The mechanics of it are very similar to those of popping up tooltip boxes over parts of the text. So parts of this article will be identical to parts of that article.

In a separate article I describe how I created the wavy button images I used on the front page of my site.

How does one create a starry, or any-other-kind-of-patterned, background for a web page?

Why, of course, by assigning the background property to the document's body:

body {background: url(NebulaElipse4/nebula01base.jpg) repeat }

nebula01base.jpg is the name of the background image. Here I describe how I created it with Gimp, a free image manipulation software package.

It probably goes without saying that the background image must be tileable. The same link above lists simple steps how to make any image tileable with Gimp.

How do we make images and explanations appear when the mouse is moved over certain sections of the page?

The "special" sections of the page where images and explanations pop up contain HTML elements to which we have assigned event handlers.

An event handler is a function that is executed when a certain event is fired. In our case, we need to handle two types of events: mouseover and mouseout. mouseover, as the name implies, fires when the user moves the mouse over an element, and mouseout, when the user moves the mouse away from the element.

Let's look at what elements there are in (a simplified version of) this page.


 <a id="a0" href="http://joom.geekitude.com/wedding_pictures.html"> 
  <img name="wedding" src="backgr200x200.gif" border="0" alt="Wedding Pictures"></a>
 <a id="a1" href="http://joom.geekitude.com/gl/public_html/index.php">
  <img name="conventions" src="backgr200x200.gif" border="0" 
          alt="Science Fiction conventions"/></a>
 <a id="a2" href="http://gallery.geekitude.com">
  <img name="photoGallery" src="backgr200x200.gif" border="0" 
	  alt="My photo gallery"/></a>

Note that all the <img> elements have the same <src> attribute, "backgr200x200.gif". backgr200x200.gif is just a completely empty, transparent 200 x 200 pixel square. All three images are empty squares.

This will make more sense when we look at their style declarations:


   img#i0 {position: absolute; left: 0px; top: 0px}
   img#i1 {position: absolute; left: 200px; top: 0px}
   img#i2 {position: absolute; left: 0px; top: 200px}

(A quick refresher on CSS syntax can be found in this article.

Each <img> element is assigned an absolute position. Their coordinates are 200 px apart horizontally and vertically from one another. So, what you first see when you come to the page is just the background, overlaid with several 200 x 200 empty transparent (and therefore invisible) squares. Very importantly, the background image, nebula01base.jpg, is also 200 x 200 big. So, the positions of the transparent squares coincide with those of the background tiles. Then, as the mouse is moved over one of those squares, the onmouseover event handling function replaces the transparent square with a wavy button image. Each wavy button image is also 200 x 200 px, and each was created from the background image, nebula01base.jpg, by putting the text in the middle of the nebula01base.jpg, and leaving the rest of it the same. So each wavy button image perfectly overlaps a background image tile.

More on how wavy button images were created: Creating Wavy Buttons.

From here on, it's just the standard image rollover Javascript trick combined with another standard pop-up box trick. A reader who knows how they are done will probably find nothing new in the rest of this article. A reader who doesn't know will probably find better explanations of image rollover and pop-up boxes elsewhere on the web. Still, I go on to describe the image rollover effect in my own words, but you've been warned -- this is probably not the most exhaustive or clearest explanation you can find.

What is the mechanism that enables a script to "replace" some images on the page with others?

Each HTML document has an images[] collection. It contains all the images in the document. Each Image object in the document.images[] collection can be referred to by name, i.e. its name attribute. For example, document["wedding"], document["conventions"], document["photoGallery"].

Each object in the document.images[] collection is an Image object. It can have various attributes, of which the most important for us is src.

At the beginning, when the document (the front page of my site) is loaded, all 3 images in the document.images[] collection have the same src attribute: backgr200x200.gif. Thus,

document["wedding"].src == "backgr200x200.gif"
document["conventions"].src == "backgr200x200.gif" 
document["photoGallery"].src == "backgr200x200.gif"  
The "static" HTML document does not contain any of the button images. To "let the document know" about the button images, we create three Image objects with Javascript:


   var wedding = new Image(); 
   wedding.src = "weddingNeb01.gif"; 
   var conventions = new Image(); 
   conventions.src = "consNeb02.gif"; 
   var photoGallery = new Image(); 
   photoGallery.src = "wbExample01SeamlessPhotoG0An.gif";

The src attribute of each of these Image objects is a wavy button image.

We also create one Image object whose source is the "invisible" square:


   var stat = new Image(); 
   stat.src = "backgr200x200.gif"; 

Its purpose, if not obvious, will become obvious later.

Now we will be able to programmatically assign an src attribute of each wavy button image to the src attribute of a corresponding static image. The code that does it is this:

document[imgName].src = eval(imgName + '.src'); 

where imgName is the name attribute of the image whose source is being replaced. If, for example, imgName == "conventions", then document["conventions"].src will be replaced by conventions.src, which is "weddingNeb01.gif". As one can see, for this to work, it is necessary that the Image objects have the same names as the name attributes of the static images they are supposed to replace.

This code is executed during an onmouseover event, hence, it must be put in the onmouseover event handler. An event handler is a function that is executed when a certain event is fired. In our case, we need to handle two types of events: mouseover and mouseout. mouseover, as the name implies, fires when the user moves the mouse over an element, and mouseout, when the user moves the mouse away from the element.

In our case, we associate the event handlers with a elements that "surround" the img elements in the HTML code of our page discussed earlier.

How do we associate an event handler with an element?

By registering the event handler to the element, for example, with this snippet of Javascript code (though there are other, obsolete as well as advanced, ways to do it.)


myAnchor.onmouseover = turnOnImgAndText;
myAnchor.onmouseout = turnOff;

In this snippet, myAnchor is a variable that represents an <a> element; turnOnImgAndText is our name for a function that replaces an invisible square with a wavy button image, and displays the explanation text. turnOff is our name for a function that hides the wavy button image and the explanation, replacing them with the invisible square. These two event handling functions need to be assigned to all three <a> elements.

Notice that there are no brackets after event handler function names! (This article on Quirksmode has a paragraph that explains why, better than I could, so I won't even bother paraphrasing it.)

We probably have more than one <a> element on our page (in this example there are 3), and we need to assign event handlers to all of them. For that, we write this Javascript function (adapted from the Quirksmode article referenced above.

In the code snippet below, numLinks is the number of <a> elements to which we want to assign this behavior, i.e. the wavy button links. (There may be other <a> elements on the page that have nothing to do with wavy buttons, so this number needs to be known in advance.)


numLinks = 3;

function assignEventHandlers()
{
  var myAnchors = new Array(numLinks);
  for (var i=0; i < numLinks; i++) {
      myAnchors[i] = document.getElementById("a" + i);
      myAnchors[i].onmouseover = turnOnImgAndText;
      myAnchors[i].onmouseout = turnOff; 
    }
}

This script makes use of an id attribute of the <a> elements, e.g. <a id="a0" href="http://joom.geekitude.com/wedding_pictures.html"> . To <a> elements of interest we have assigned ids a0, a1, a2, etc., which allows us then to get "handles" on those <a> elements by caling getElementById method like this:


myAnchors[i] = document.getElementById("a" + i);

Calling this method in a loop with i going from 0 to numLinks allows us to assign event handlers to all the <a> elements of interest.

This function will need to be called after the document has been loaded, because at that time the <a> elements will have been initialized. To make that happen, I put this code snippet right before the closing </body> tag:


<script type="text/javascript">
  onload = assignEventHandlers();
</script>

What goes on inside event handler functions turnOnImgAndText and turnOff?

turnOnImgAndText does 2 things: as we said before, it replaces an "invisible" image with a wavy button image, and it also displays an explanation. Now that we looked at the mechanism that replaces images, we need to examine how the explanation is displayed. What kind of beast -- or what kind of HTML element -- is it?

In our case, each explanation "lives" inside a <div> element. (It can probably live inside other types elements too, but I think <div> is a natural choice). With a <div> tag you can create page elements that overlap other elements on the page. You can also assign them a style property called visibility. Our wavy button explanations are nothing else than <div> elements that are made visible sometimes -- when the user moves the mouse over a certain area of the page -- and are invisible the rest of the time.

Here is an example of a function that creates a <div> element. (This function was inspired by www.scottandrew.com article on DOM windows.

function CreateExplanation(w, h, text){
  var explSquare = document.createElement("div");
  explSquare.style.position = "absolute";
  explSquare.style.width = w + "px";
  explSquare.style.height = h + "px";
  explSquare.style.backgroundColor = "transparent";
  explSquare.style.visibility = "hidden";
  explSquare.style.padding= "0px 20px 0px 20px"

  explSquare.innerHTML = "<p class=\"explText\">" + text  + "</p>";
  return explSquare;
}

This function takes the following arguments:

  • w: width of the explanation box;
  • h: height of the explanation box;
  • text: text the explanation box will contain.

The code is pretty much self-evident. The first line of the function creates an element with <div> tag. The rest of the lines assign width, height, and background color ("transparent") to the corresponding style properties of this element. Its visibility is initially set to "hidden", because we want the explanation to show up only when the mouse is moved over a certain part of the text, and remain hidden the rest of the time.

To create 3 of these <div> elements we will invoke the function CreateExplanation 3 times in a for loop. We want all the explanation boxes to be 200 x 200 pixels, not for any good reason, but simply because everything on this page is 200 x 200 px. There's no reason they can't be different size, of course. In the example below, explanations is an array of explanation texts. After creating explElems -- an array of 3 <div> elements that contain explanation texts -- we append each of those elements to the document body:


explanations = 
["Pictures from my wedding", 
"Science fiction conventions I've been to, authors and interesting people I've met",
"The visual accompaniment of my life"];

var explElems = new Array(numLinks);

for (var i=0; i < numLinks; i++) {
  explElems[i] = new CreateExplanation(200, 200, explanations[i]);
}

Of course, the explanation needs to pop up in the right place: for example, below the button, or to the side. In our case, the coordinates of the explanation box are predetermined in advance, and don't need to be calculated on the fly. We have decided that for all the buttons in the top row, the explanation will pop up underneath the button, and for the button in the second row, the explanation will pop up to the right. So, we create two arrays with the x and y coordinates of the top left corners of the explanation <div> elements:


boxLeft = [ "0", "200",  "200"];
boxTop  = ["200", "200", "275"];

The onmouseover event handler function turnOnImgAndText will use the values of these arrays to decide where to pop up an explanation.

This is what the code of turnOnImgAndText looks like.


function turnOnImgAndText()
{ 
 i = this.id.substring(1);
 imgName = document.images[i].name;
 turnOn(imgName); 
 explElems[i].style.left = boxLeft[i] + "px";
 explElems[i].style.top = boxTop[i] + "px";
 explElems[i].style.visibility = "visible";
}

The this object in this function is an <a> element on which this event handler is invoked. Each of the <a> elements of interest to us has an id attribute: a0, a1, etc. So, the line i = this.id.substring(1); gets the number embedded in the id attribute and assigns it to i. The second line gets the name attribute of the i'th element of the document.images[i] array[, which is the name attribute of the ith image]. Then it calls the turnOn function. This function displays a wavy button image whose name attribute is contained in imgName. The next 2 lines set the x and y coordinates of the top left corner of explElems[i] -- the <div> element that contains the explanation text for this link.

The last line sets the visibility attribute of the <div> element to visible, thereby displaying the explanation text.

Here is the turnOn function. It contains only one line of code, that was explained earlier.

function turnOn(imgName) { 
   if (document.images) document[imgName].src = eval(imgName + '.src'); 
} 

And here is the turnOff function.


function turnOff() { 
   i = this.id.substring(1);
   imgName = document.images[i].name;
   if (document.images) { 
      document[imgName].src = stat.src; 
   }
   explElems[i].style.visibility = "hidden";
} 

As in the turnOnImgAndText function, the first line gets the number embedded in the id attribute and assigns it to i. The second line gets the name attribute of the i'th element of the document.images array[, which is the name attribute of the ith image]. The 4th line -- document[imgName].src = stat.src; replaces one image with another the same way function turnOn does. At the time when turnOff is called -- when the user moves the mouse off of [an <a> element | a link] -- document[imgName] is a button image, and it needs to be replaced with the invisible square that occupied this spot in the beginning and should occupy it at all times except when the mouse is over the link. As noted earlier, the source of the Image object stat is backgr200x200.gif, the invisible square. It will be substituted for a button image when this function is executed.

The last line sets the visibility attribute of the <div> element to visible, thereby displaying the explanation text.

The complete sample page, including Javascript and HTML code

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head><title>Sample page</title>
   <STYLE TYPE="text/css">
   body {background: url(nebula01base.jpg) repeat }
   img#i0 {position: absolute; left: 0px; top: 0px}
   img#i1 {position: absolute; left: 200px; top: 0px}
   img#i2 {position: absolute; left: 0px; top: 200px}
   div.explanation {position: absolute; width: 200px; height: 200px; 
		    background-color: transparent}
   p.explText {font-family: sans-serif; color: #8fafc5}
   </STYLE>
</head>
<body>

<script type="text/javascript">

textClass="explText";

// Explanation texts that will appear next to a button when 
// the mouse is moved over the button:

explanations = 
["Pictures from my wedding", 
"Science fiction conventions I've been to, authors and interesting people I've met",
"The visual accompaniment of my life"];

// the x-coordinates of top left corners of explanation elements
boxLeft = [ "0", "200",  "200"]; 
// the y-coordinates of top left corners of explanation elements
boxTop  = ["200", "200", "275"]; 

numLinks = 3;

function CreateExplanation(w, h, text){
  var explSquare = document.createElement("div")
  explSquare.style.position = "absolute"
  explSquare.style.width = w + "px"
  explSquare.style.height = h + "px"
  explSquare.style.backgroundColor = "transparent"
  explSquare.style.visibility = "hidden"
  explSquare.style.padding= "0px 20px 0px 20px"
  explSquare.innerHTML = "<p class=\"explText\">" + text  + "</p>";
  return explSquare
}

function turnOnImgAndText() 
{ 
 i = this.id.substring(1);
 imgName = document.images[i].name;
 turnOn(imgName); 
 linkTexts[i].style.left = boxLeft[i] + "px";
 linkTexts[i].style.top = boxTop[i] + "px";
 linkTexts[i].style.visibility = "visible";
}

function turnOn(imgName) { 
   if (document.images) document[imgName].src = eval(imgName + '.src'); 
} 

function turnOff() { 
   i = this.id.substring(1);
   imgName = document.images[i].name;
   if (document.images) { 
      document[imgName].src = stat.src; 
   }
   linkTexts[i].style.visibility = "hidden";
} 

function initLinkBehavior()
{
  var anch = new Array(numLinks);
  for (var i=0; i < numLinks; i++) {
      anch[i] = document.getElementById("a" + i);
      anch[i].onmouseover = turnOnImgAndText;
      anch[i].onmouseout = turnOff; 
    }
}

var linkTexts = new Array(numLinks);

for (var i=0; i < numLinks; i++) {
  linkTexts[i] = new CreateExplanation(200, 200, explanations[i]);
}

if (document.images) { 
   var stat = new Image(); 
   stat.src = "backgr200x200.gif"; 
   var pictures = new Image(); 
   pictures.src = "weddingNeb01.gif"; 
   var conventions = new Image(); 
   conventions.src = "consNeb02.gif"; 
   var photoGallery = new Image(); 
   photoGallery.src = "wbExample01SeamlessPhotoG0An.gif";
} 

for (var i=0; i < numLinks; i++) {
  document.body.appendChild(linkTexts[i]);
}

</script>

 <a id="a0" href="http://joom.geekitude.com/wedding_pictures.html"> 
  <img id="i0" name="pictures" src="backgr200x200.gif" border="0" 
	  alt="Wedding Pictures"></a>
 <a id="a1" href="http://joom.geekitude.com/gl/public_html/index.php">
  <img id="i1" name="conventions" src = "backgr200x200.gif" border = "0" 
	  alt = "Science Fiction conventions"/></a>
 <a id="a2" href="http://gallery.geekitude.com">
  <img id="i2" name="photoGallery" src = "backgr200x200.gif" border="0" 
	  alt="My photo gallery"/></a>

<script type="text/javascript">
  onload = assignEventHandlers();
</script>

</body>

</html>

Last Updated ( Monday, 12 June 2006 )
 
< Prev   Next >

(C) 2012 Elze's CMS experiment
Joomla! is Free Software released under the GNU/GPL License.