A user of my jQuery plug in ImageMapster, while working through a problem, asked if it was possible to set up an imagemap that you could drop things onto. I had never done this before, but it seemed like an interesting problem with lots of potential applications, so I set about trying to solve it. A the bottom of this page is a simple example using Marvin the Martian that works, and I'll explain how before we get there.
Using jQueryUI's draggable
method, it's easy to create things that can be dragged. Things get a little tricky because of z-index
es though. When you drag something, it needs to have a highest z-index
on the page, or it will disappear behind things when you drag it over them. But at the same time, an imagemap will not activate unless it has the highest z-index.
A paradox!
Luckily, when using ImageMapster, things are already set up so you can have your dragging element on top, and still have an active imagemap. This is the very nature of how it works: when binding an image, ImageMapster makes a copy of the image to use as a backdrop, and then makes your original image invisible though CSS using opacity:0
. So all you need to do is make sure the z-index of your drag element is between those two things. A little understanding of ImageMapster's element scheme is all that's needed:
The topmost layer, the HTML image map itself, isn't really a layer in that you don't need to set the z-index
of the <area />
elements explicitly. However, it does act like a layer in that mouse events over the areas will supercede events over the image itself. But basically, the original image is the topmost element.
Finally, all this stuff is wrapped up in a div
. This is useful to know because it means you can use jQuery's siblings
method to easily change the z-index
of everything that's important except your original image.
The code below is the basic logic you'll need to make something draggable over a live imagemap.
var img = $('#my-image-map'), item = $('#draggable-object');
img.mapster();
...
img.siblings().css('z-index',0);
img.css('z-index',10);
item.css('z-index',5);
That's almost all you need to do. What happens when you drop the martian somewhere? It now has a z-index that is lower than the original image. Even though the imagemap is not visible, and the martian is, you won't be able to grab the martian again, because once you drop it, it's old z-index takes effect.
To address this, you need a little more sleight of hand. When someone first grabs the draggable element, change its z-index
to be a value between the two image layers. When they drop it, though, change it to something that's higher than the original image, so it will be on top and can be picked up again.
In Action!
Here's the functioning example, all the code follows (or just use your browser to look at it). Drag the martian onto Mars to "win
". Any other planets will give you a negative response, and nothing happens if you drop him in space. I've also set it up on jsfiddle.net. Enjoy!
Note: This originally appeared as a blog post and requires JavaScript includes for the demo to work. Please go there to try the demo or use the link to the jsfiddle above -- it does not work here on CodeProject
Help me get home!
Code
Internet Explorer notes: This doesn't seem to work in IE < 9 -- in that the imagemap areas are not activated on mouseover while dragging. I'll address this, and present a solution, in my next post.
<br />
var img = $('#planets'), martian=$('#martian');
img.mapster({
mapKey: 'alt',
fillOpacity: 0.8,
fillColor: 'ff0000',
stroke: true,
strokeWeight: 2,
strokeColor: '00ff00',
onConfigured: function() {
// this will be called only after ImageMapster is done setting up
img.siblings().css('z-index',0);
img.css('z-index',10);
}
});
martian.css('z-index',5)
.draggable({
drag: function() {
$(this).css('z-index', 5)
}
});
img.droppable({
drop: function(e,ui) {
// set z-index to the highest, so it can be dragged again
$(ui.draggable).css('z-index',20);
// returns the mapKey value of the currently highlighted item
var landing=img.mapster('highlight');
if (landing=='Mars') {
alert("Thanks for bringing me home!!!");
martian.draggable('disable').hide('slow');
} else if (landing) {
alert("I don't live on " + landing);
}
}
});