View the Facebook-esque Photo Tagging demo

Example vs. Facebook

In this article I'll show you how to create a Facebook-like photo tagging feature. If you're like me you're probably more interested in a live example than digging through this entire article so here is a live demo of the above example.

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 >>