Headbank digital
Design

Spot The Ball, JavaScript stylee

Where has little Timmy's ball gotten to? Quick, he's about to cry! Click on the image where you think the ball is.


Click to win!

If you didn't get it, don't feel too bad. (I didn't have a prize anyway.) If you did, well done! Hope you didn't cheat by looking at the code.

This is one of my favourite discoveries in HTML since I began learning it. You might expect a whopping amount of Javascript lurking behind the 'toy' on this page, but in fact the hard work is done by a humble HTML element attribute. The 'sending' code is as simple as this:

<a href="img_ismap.htm"><img src="images/spottheball.jpg" border="0" ismap="ismap" /></a>

The link on the image, and the ismap attribute, mean that wherever you click on the image, those coordinates are sent to the destination page as URL parameters. That page (or this page, in my drive to keep things simple) can easily extract and process them either server-side or client-side.

Obviously, with server-side scripting and a database to play with, there's the potential for some very sophisticated image-mappery. As I've only got JavaScript to play with, this is a simple object-lesson in how to utilise this method and perform a simple visual interaction with it.

The link's format, as you'll see if you mouseover the image, has the format targetpage.htm?x,y so it's a little simpler than the contents of a GET-method form submission - for JavaScript at least. Server-side languages like PHP are equipped to automatically parse a URLencoded string like targetpage.htm?var1=x&var2=y into an associative array. You could access these variables quite easily like so:

x = $_GET['var1']; y = $_GET['var2'];

The limitation of JavaScript is it treats the whole parameter segment of the address as one string (accessed as location.search), so some more complex scripting is needed to access the variables in GET format. NOTE: this code is slightly sub-optimal for the sake of transparency.


var str = location.search.substr(1); //lose the question-mark

str = unescape(str); //translate any URLencoded characters

allParams = str.split('&'); //create an array of variable-value pairs

var keys = new Array(allParams.length); //initialise 2 arrays
var values = new Array(allParams.length);

for (i=0; i<allParams.length; i++)
 {
 var j = allParams[i].split('='); //separate the variable and value
 keys[i] = j[0]; //fill the arrays with keys and values
 values[i] = j[1];
 }

// Now we'll use our two arrays to display
// a list of variables and values.

document.write('<h3>URL Parameters Received</h3>\n');
document.write('<ul>\n');
for (t=0; t<keys.length; t++)
 document.write('<li>'+keys[t]+' = '+values[t]+'</li>\n');
document.write('</ul>\n');

I've seen bigger scripts, but it's quite a lot of work just to make some form data available for use. Thankfully, in our particular setup there's slightly less work to do:


var str = unescape(location.search.substr(1));

var allParams = str.split(',');

document.write('<h3>Coordinates Clicked by You:</h3>\n');
document.write('<ul>\n');
document.write('<li>X-coordinate = '+allParams[0]+'</li>\n');
document.write('<li>Y-coordinate = '+allParams[1]+'</li>\n');
document.write('</ul>\n');

Delightfully minimal. Let's continue by looking at how I used these values for the game above. Just imagine this is being processed server-side so you can't just cheat by reading the code!

The 'hotspot' area on the image is a rectangle defined by maximum and minimum values for X and Y. If the destination page is a different page, there's nothing on the source page to give away this definition. The destination page evaluates whether you clicked within that rectangle, but it also displays a visual indicator to show where you clicked.

The visual resides in an absolutely-positioned DIV which is empty unless the location.search string is non-empty. The DIV, which I've called 'spot' below, has to have its position assigned relative to both the received values and the position of the image on the page, and its image defined by whether or not you hit the target. Note that this is a case of writing an HTML <img> tag within the DIV, because there is no cross-browser DOM object for the background-image.


if(location.search !== '')
 {
 var str = unescape(self.location.search.substr(1));
 var allParams = str.split(',');

 //set DIV's position by adding parameters to image's position
 //note: shave off 15px so spot appears under cursor
 var x = parseInt(allParams[0]);
 var theX = x + document.getElementById('spottheball').offsetLeft -15;
 var y = parseInt(allParams[1]);
 var theY = y + document.getElementById('spottheball').offsetTop -15;

 //check whether click falls within rectangle
 if ((x < 270) && (y < 30) && (x > 260) && (y > 20))
  var res = 'right';
 else
  var res = 'wrong';

 var theSpot = document.getElementById('spot');
 theSpot.style.left = theX+'px';
 theSpot.style.top = theY+'px';
 theSpot.innerHTML = '<img src="images/'+res+'.gif" border="0" />';
 }

For all-round success with this script, placing it inside a function is a good idea. That means it can be called by body onload and onresize, which will ensure that the render-time dimension values I've used are defined before execution. My image is centred, so that's important to keep the spot matching the same position on the image.

The one downside of this design is that once you've clicked once, the spot obscures part of the image so you can't click within that area. But hey, maybe you only want to give your contestants one try...