I recently watched parts of the Google I/O Keynote. In the little demos they showed at the very beginning of the video, there was a little app that followed the mouse around and created waves wherever the mouse went. I wanted to do something similar, so here is a (quite lengthy, in-depth) tutorial.
This tutorial assumes you know enough about jQuery to understand what the $(function(){}) is used for and all associated $(“”) calls do. I will explicitly tell you what can be OUTSIDE of this anonymous function. Otherwise assume it is inside the jQuery function.I like to declare all my variables outside the jQuery function so that in case my other functions need them. My preference, and is not strictly necessary.
You can view the demo here

Or you can download the source. (tar file containing only wave.html)
Step 1: HTML
First we need the very basic html to set the page up.
<html>
<head>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script type="javascript/text">
</head>
<body>
<div id="click">Click Anywhere<br>Press 'c' toggle color changing</div>
<div id="main">
</div>
</body>
</html>
That’s it. Step 1 done. Next.
Step 2: Basic styling
Another easy step, adding the CSS so that the bars look like how we want. As well as the little notifications at the top.
#main {
width: 98%;
height: 100%;
padding: 0;
margin: 0;
}
#click {
position: absolute;
left: 50%;
z-index: -1;
}
Step 3: Creating the bars
Now we need to start by making something appear on the page.
We need to find out how many bars we need to cover the entire page. The variable set here, ‘divs’ needs to be instantiated OUTSIDE the jQuery function due to it being used by another function outside as well.
// you can make them as wide or as skinny as you like. I personally like 12px wide. var width = 12; divs = ($(window).width() - (width-2)) / (width+1);
Then we create the html to be inserted to our #main div and insert it there.
var index = 0;
while (index < divs){
new_divs += "<div id='div" + index + "'></div>";
index++;
}
$("#main").html(new_divs);
Now we can add some styling to them. This could probably be done a bit clearer with some classes, but the width and left offset needs to be set dynamically anyway, so I chose to do this all at once.
var left_offset = 0;
$("#main div").each(function(){
$(this).css({
'position': 'absolute',
'height':'50%',
'width': width+'px',
'padding':'0',
'margin': '0',
'background-color': color,
'bottom': '0px',
'left': (left_offset * (width+1))
});
$(this).attr("rel", 0);
left_num++;
});
Setting the ‘rel’ of the divs will come in handy later when we want the behavior to function properly. Now we can work on more of the business logic.
Step 4: Handling a click
We are now going to create a function waver(e) that gets called on every click. (actually, it’s a document.mousedown event… advantages of this explained later).
When the person clicks, we want a couple things to happen: the height of the click to be stored, the closest div to the click needs to raise up to that point, and all the other divs to the left and right need to recursively animate up to that poisition.
First we need to know if we’re in an IE browser or not. Simple detection of a document.all field:
var IE = document.all?true:false
Then the detection of mouse X and mouse Y. Typically, with the IE version, you need to offset by document.body.scrollLeft and .scrollTop. However, because our bars will never be more than the window width, and never more than the window height, we don’t need to do this. Also, this is where calling the clicking event on document.mousedown comes in handy. When the mousedown even fires, it passes a parameter Event (here used as ‘e’) that contains all kind of useful information. You’ll notice that the IE version doesn’t use that. That is because the event. is a built in variable that contains all the info we need.
if (IE) {
var x = event.clientX;
var y = event.clientY;
}
else {
var x = e.pageX;
var y = e.pageY;
}
Now that we have the location of the mouse, we can update the closest div, and all the other ones around it. We get the ID of the closes div by iterating though all of them and finding if the x-value of the mouse is inside the left and right of it. here, width+2 compensates for the extra room on either side of the bars. Also, a substr is necessary because .css(‘left’) returns ‘XXpx’. The other substr is because our divs are names ‘divID’. This is a design choice, and could be done a myriad of other ways.
height = (document.height - y + 17);
$("#main div").each(function(){
var templeft = $(this).css('left');
templeft = parseInt(templeft.substring(0, (templeft.length - 2)));
if ((x >= templeft) && (x <= (templeft+width+2))){
id = $(this).attr('id');
id = parseInt(id.substring(3));
}
});
$("#div" + id).animate({'height': height}, 400);
Step 5: Color
This is more a fun feature than something that is necessary, but included in the tutorial for completeness' sake. We are going to create a function (outside the anonymous jQuery function) that detects if the 'c' key is pressed on every document.onkeyup.
NOTE: In the file included at the top, you will notice that "document.onkeyup = logger" is located inside the jQuery function. This is not necessary, it can be outside as well.
document.onkeyup = logger;
var toggle_color = false;
function logger(e){
if (e.keyCode == 67){
toggle_color = !(toggle_color);
}
}
This allows a person to toggle the color changing function (initially turned off). Now, inside the waver function, we need to set the color based on a randomly generated number between 0 and 255.
NOTE: This step is a rather poorly implemented version of generating 3 random numbers. There are much better ways to do this, but I chose this way for clarity as well as ease.
if (toggle_color){
var red = Math.round((Math.random()*10000) % 255);
var blue = Math.round((Math.random()*10000) % 255);
var green = Math.round((Math.random()*10000) % 255);
color = "rgb("+red+", "+blue+", "+green+")";
}
Step 6: Recursion
Now we have everything we need to start recursing over the rest of the bars and adjusting their height. At the very bottom of the waver function, we are going to put two setTimeout calls. One to do the left (moveleft()) side, and one to the right (moveright()).
It's right about this juncture in our tutorial that I should explain something about a priority system. In my initial versions of this script, waves reacted with each other in unfavorable ways. Things would go haywire and I would end up with uneven bars all the way across because if you clicked on the far left high, then quickly on the far right low, you would end up with half the bars high and half the bars low.
To fix this, you must give the last click the highest priority, and if another wave is encountered, the most recent click will be animated. A variable must be declared (here, count) and then every time waver is called, incremented. This new count is then passed on to the recursive functions and used as the priority number. (There is one caveat to this, that I cannot explain... if you click on a bar in the middle of being animated, it goes back to it's original position, not the newest clicked position).
These four lines should be the last lines inside the waver function. They use the divs immediately to the left and right of the one clicked on as their starting point.
left = id-1;
right = id+1;
setTimeout(function(){moveleft(left, height, count, color)}, 50);
setTimeout(function(){moveright(right, height, count, color)}, 50);
Step 7: The rest of the bars
Now we need two separate functions (not really, it can be done in one, but for organization's sake, let's do two) that handle the left and right sides of the clicked bar. In my source, they are placed outside the jQuery function because I like to be difficult. Again, this is not strictly necessary.
They do the following: check the bar's current priority. If the bar's current priority (this_count) is less than the priority of the current wave (orig_count) we animate the height of the bar and change it's color (if needed). Then we decrement (or increment) the appropriate div id, and call the function again, keeping the same priority count, color, and height.
function moveleft(left_num, h_num, orig_count, this_color){
this_count = $("#div"+left_num).attr("rel");
// compensate for priority
if (left_num >= 0 && (this_count < orig_count)){
$("#div"+left_num).css("background-color", this_color);
$("#div"+left_num).attr("rel", orig_count);
$("#div"+left_num).animate({'height': h_num}, 400);
left_num--;
setTimeout(function(){moveleft(left_num, h_num, orig_count, this_color)}, 40);
}
}
This is obviously on the left side. The right side is conceptually similar, but with the var name right_num. right_num must be less than divs (variable created in 3rd step) and right_num is incremented just before the recursive call.
Finished Product
Now after creating the moveright() function, everything is done! You can check out the demo or download the source. Enjoy.
If you have any questions, comments, or can fix my bug (seen in step 6), leave it in the comments or email me at hello at andrebluehs dot net.
Follow me on twitter: @helloandre