Importing the jQuery Library
As with all my examples using jQuery, I link to Google's hosted version. If a user already has the file in their browser cache, it cuts down on another HTTP request thus saving your users time.
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" type="text/javascript"></script>
HTML Setup
Below is all the HTML needed for my image tagging example. I dynamically insert much of the other needed HTML via jQuery. Originally I planned to make this a jQuery plugin. This could still easily be accomplished by taking the code within $(document).ready & adding it into a standard plugin code block.
<img src="image.jpg" style="width: 604px; height: 423px;">
CSS Setup
Unlike many of my other articles, there is a lot more code needed to achieve the desired effect. Because of this I probably won't walk you through line-by-line. Instead I'll give a brief description for some of the main elements in the DOM.
#tag-wrapper - This is wrapped around the IMG element and is given a relative position. This allows us to position other elements absolute.
#tag-target - This is a DIV which boxes in the target which a user has clicked on within the photo.
#tag-input - This is a DIV which contains a label, input box & submit/reset buttons. It is attached to the #tag-target DIV.
.hotspot - Finally after a tag has been assigned, a .hotspot DIV is created and positioned over the desired location on the image.
html, body { margin: 0px; padding: 0px; }
body
{
color: #666;
font-size: 12px;
font-family: Arial, Helvetica, sans-serif;
}
#tag-wrapper
{
border: 1px solid #ccc;
box-shadow: 0px 0px 10px #bbb;
-webkit-box-shadow: 0px 0px 10px #bbb;
-moz-box-shadow: 0px 0px 10px #bbb;
display: block;
padding: 10px;
position: relative;
}
#tag-target, #tag-wrapper img { cursor: crosshair; }
#tag-wrapper img { position: absolute; }
#tag-target
{
display: none;
border: 4px solid #fff;
box-shadow: 0px 0px 20px #000;
-webkit-box-shadow: 0px 0px 20px #000;
-moz-box-shadow: 0px 0px 20px #000;
height: 100px;
width: 100px;
position: absolute;
top: 0px;
left: 0px;
z-index: 2;
}
#tag-input
{
background: #fff;
display: none;
padding: 5px;
position: absolute;
top: 0px;
left: 0px;
width: 137px;
z-index: 2;
}
#tag-input label
{
display: block;
font-weight: bold;
}
#tag-input input
{
border: 1px solid #ccc;
color: #888;
display: block;
margin: 5px 0px;
outline: 0px;
padding: 3px;
width: 124px;
}
.hotspot
{
border-width: 0px;
box-shadow: 0px 0px 0px #000;
-webkit-box-shadow: 0px 0px 0px #000;
-moz-box-shadow: 0px 0px 0px #000;
height: 100px;
width: 100px;
position: absolute;
}
.hotspot:hover, .hotspothover
{
border: 4px solid #fff;
box-shadow: 0px 0px 20px #000;
-webkit-box-shadow: 0px 0px 20px #000;
-moz-box-shadow: 0px 0px 20px #000;
z-index: 1;
}
.hotspot span { display: none; }
.hotspot:hover span, .hotspothover span
{
background: #fff;
display: block;
font-weight: bold;
padding: 3px 0px;
text-align: center;
}
.remove { color: #748950; cursor: pointer; text-decoration: underline; }
Global Javascript Variables
Below are the global Javascript variables used. targetX & targetY plot coordinates for where the user clicks on the image. tagCounter is used to give each of the tags a unique ID
//Placed outside .ready for scoping
var targetX, targetY;
var tagCounter = 0;
$(document).ready Javascript
In the main code block I've liberally added comments to explain each line of code:
$(document).ready(function(){
//Dynamically wrap image
$("img").wrap('<div id="tag-wrapper"></div>');
//Dynamically size wrapper div based on image dimensions
$("#tag-wrapper").css({width: $("img").outerWidth(), height: $("img").outerHeight()});
//Append #tag-target content and #tag-input content
$("#tag-wrapper").append('<div id="tag-target"></div><div id="tag-input"><label for="tag-name">Person\'s Name:</label><input type="text" id="tag-name"><button type="submit">Submit</button><button type="reset">Cancel</button></div>');
//$("#tag-wrapper").click(function(e){
$("img").click(function(e){
//Determine area within element that mouse was clicked
mouseX = e.pageX - $("#tag-wrapper").offset().left;
mouseY = e.pageY - $("#tag-wrapper").offset().top;
//Get height and width of #tag-target
targetWidth = $("#tag-target").outerWidth();
targetHeight = $("#tag-target").outerHeight();
//Determine position for #tag-target
targetX = mouseX-targetWidth/2;
targetY = mouseY-targetHeight/2;
//Determine position for #tag-input
inputX = mouseX+targetWidth/2;
inputY = mouseY-targetHeight/2;
//Animate if second click, else position and fade in for first click
if($("#tag-target").css("display")=="block")
{
$("#tag-target").animate({left: targetX, top: targetY}, 500);
$("#tag-input").animate({left: inputX, top: inputY}, 500);
} else {
$("#tag-target").css({left: targetX, top: targetY}).fadeIn();
$("#tag-input").css({left: inputX, top: inputY}).fadeIn();
}
//Give input focus
$("#tag-name").focus();
});
//If cancel button is clicked
$('button[type="reset"]').click(function(){
closeTagInput();
});
//If enter button is clicked within #tag-input
$("#tag-name").keyup(function(e) {
if(e.keyCode == 13) submitTag();
});
//If submit button is clicked
$('button[type="submit"]').click(function(){
submitTag();
});
}); //$(document).ready
Functions
Finally, here is a few functions I use for adding/removing tags:
function submitTag()
{
tagValue = $("#tag-name").val();
//Adds a new list item below image. Also adds events inline since they are dynamically created after page load
$("#tag-wrapper").after('<p id="hotspot-item-' + tagCounter + '">' + tagValue + ' <span class="remove" onclick="removeTag(' + tagCounter + ')" onmouseover="showTag(' + tagCounter + ')" onmouseout="hideTag(' + tagCounter + ')">(Remove)</span></p>');
//Adds a new hotspot to image
$("#tag-wrapper").append('<div id="hotspot-' + tagCounter + '" class="hotspot" style="left:' + targetX + 'px; top:' + targetY + 'px;"><span>' + tagValue + '</span></div>');
tagCounter++;
closeTagInput();
}
function closeTagInput()
{
$("#tag-target").fadeOut();
$("#tag-input").fadeOut();
$("#tag-name").val("");
}
function removeTag(i)
{
$("#hotspot-item-"+i).fadeOut();
$("#hotspot-"+i).fadeOut();
}
function showTag(i)
{
$("#hotspot-"+i).addClass("hotspothover");
}
function hideTag(i)
{
$("#hotspot-"+i).removeClass("hotspothover");
}
Above & Beyond
I did add a few effects that Facebook does not have. For instance I added fading effects on pretty much everything I could. I often find that adding a fade or slide effect gives applications a more "polished look". I also added an animation if you click a new location for the target tag. Once again, here's a link to the finished product >>