Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
Ben Nadel at CFUNITED 2010 (Landsdown, VA) with: Vicky Ryder
Ben Nadel at CFUNITED 2010 (Landsdown, VA) with: Vicky Ryder@fuzie )

jQuery Photo Tagger Plugin For Flickr-Style Photo Tagging

By Ben Nadel on

A few days ago, I programmed a little proof-of-concept for Flickr-style photo tagging using jQuery. I did it as an exploration in mouse-based event binding; but, it was a lot of fun and I wanted to see if I could take it a step further. So, yesterday and this morning, I packaged the jQuery code up into a jQuery plugin (phototagger.jquery.js), build a light-weight ColdFusion persistence layer (drop-and-run, no database required), and turned it into an official project: jQuery Photo Tagger.

jQuery Photo Tagger Plugin Video Demo. 

Watch the online video here.

Try the online demo here (NOTE: Hold CTRL key when clicking mouse to create hotspot).

View the official project page here.

There's way too much code to show here - the jQuery plugin comes in at about 1,000 lines of code. As such, you can either check out the project page or try the online demo for yourself. For now, I'm just going to show you the HTML that actually sets up and invokes the Photo Tagger jQuery plugin:

  • <html>
  • <head>
  • <title>Flickr-Style Photo Tagging With jQuery And ColdFusion</title>
  • <style type="text/css">
  • {
  • float: left ;
  • margin-right: 10px ;
  • }
  • </style>
  • <script type="text/javascript" src="./jquery-1.4.1.js"></script>
  • <script type="text/javascript" src="./phototagger.jquery.js"></script>
  • <script type="text/javascript">
  • // When the DOM is ready, initialize the scripts.
  • jQuery(function( $ ){
  • // Set up the photo tagger.
  • $( "" ).photoTagger({
  • // The API urls.
  • loadURL: "./load_tags.cfm",
  • saveURL: "./save_Tag.cfm",
  • deleteURL: "./delete_tag.cfm",
  • // This will allow us to clean the response from
  • // a ColdFusion server.
  • cleanAJAXResponse: function( apiAction, response ){
  • // Check to see if this it the load.
  • if (apiAction == "load"){
  • // Loop over response array.
  • $.each(
  • response,
  • function( index, tagData ){
  • // Translate the ColdFusion keys
  • // to lowercase. This will create
  • // dupliate keys, but it doesn't
  • // matter for our use-case.
  • $.each(
  • tagData,
  • function( key, value ){
  • tagData[ key.toLowerCase() ] = value;
  • }
  • );
  • }
  • );
  • }
  • // Return cleaned response.
  • return( response );
  • }
  • });
  • });
  • </script>
  • </head>
  • <body>
  • <h1>
  • Flickr-Style Photo Tagging With jQuery And ColdFusion
  • </h1>
  • <div class="photo-container">
  • <img id="photo1" src="./sexy.jpg" width="520" height="347" />
  • </div>
  • <div class="photo-container">
  • <img id="photo2" src="./sexy2.jpg" width="520" height="347" />
  • </div>
  • </body>
  • </html>

I tried to build the jQuery plugin with as much flexibility as I could. There are a large number of default settings that can be overridden globally or on an as-needed basis (as done in the code above). Because the photo tagging life-cycle requires persistence, you do have to provide it with load, save, and delete URLs.

This is probably the most complicated piece of jQuery that I have ever created; as such, writing this was a lot of trial an error. The approach uses a combination of standard jQuery plugin architecture and object oriented programming (OOP). The plugin defines a private controller class and then instantiates a new instance of that controller class to be used for every photo-tagging element on the page. This felt really good to do, but I have not idea how this falls in terms of jQuery plugin best practices.

Right now, it's a bit buggy in IE. IE doesn't seem to like mouse over events on clear background. IE also seems to hate moving the mouse while the CTRL key is pressed. I'll have to do some digging to see if there are ways around this, or if I simply have to find a different approach. At least now that this is an official project, it gives me a place to post my updates in a meaningful manner.

If anyone has any suggestions about this or things that they would like added / changed, please let me know.

Reader Comments

It be nice if it was written somewhere you create a tag by holding CTRL and left-click dragging. Didn't figure it out until I read your explanation about IE not liking CTRL + mouse drag. :)

This is largely preference, but in things like tags or tooltips, I like being able to select the text given. Allowing that would open up the possibility to provide links in the tag.

Regardless, very cool project. Thanks for sharing!


Ahh, nice catch. I have added a note next to the demo link. I am not sure what you mean about selecting the text given? Can you explain that more?


Thanks my man :)


Sure thing. For example, say you have a photo in which someone's using a specific workout product, and you tag the photo so people know what they're using. Now another user comes by, checks out the photo, and sees the tag. If they want to do a search to find out more about that product, they have to mouse over the tag and write down the name, whereas if they could just select the text, it'd be a lot easier.

It's a really lame example, but hopefully it illustrates the point. :)

Also, any plans to allow HTML into the tags? Maybe an option to enable/disable HTML, along with a list of accepted elements? You could tag a photo with a link to somewhere (product, photo credit, person's profile, etc) that, with selecting text possible, would allow a user to explore the subject further.

Hope these suggestions don't detract from what's already a very cool plugin.

For what it's worth, Flickr uses a button on the page to turn on tag creation, and allows simple html elements to be included, like links and strong/em tags.

Ben.. you rock! I was actually JUST working on something very similar for our advertising clients to highlight image points they want to specify in the images..this will prove to be a step beyond my attempt.. thank you so very much dude.

Great plugin but for Mac users the ctrl key triggers the right-click contextual menu which gets in the way a little. You may need to add the ability for Mac users to use the cmd key instead?


I think the option to allow for HTML is a good idea; as well is the idea to have a list of "allowed" HTML tags. Very cool.


Hmm. Maybe I need a way to have the creation turned of tag hotspots on / off. Not sure what kind of event would / should trigger that.


Cool man - let me know if you see room for more stuff in this. I have some updates to do to make this more IE-friendly.


If I end up going with a on/off mechanism for the hotspot creation, then this won't be an issue. IE seems to not like the CTRL+mouse-move anyway.


It sort of works in IE, but not well. As it currently stands, only the border of the box triggers the message. It is definitely junky, though. I'm coming up with ways to improve it.

Very cool work Ben. I checked it the other day but I'm on a Mac so it wasn't working quite right as Dave commented. I pulled the code to see what I could do, but then I came back today and see you added the on/off feature.. I like that option, good fix. Worked well in Safari, Chrome, FF, Opera. Can't wait to see what's next..


Awesome - but just to be clear, are you saying that the newest updates are working for you on the Mac? Or, are you seeing any current bugs?

Yes, its working now on my mac now that I don't have to control click to tag because you added the enable/disable. Hope that helps!

This is cool, but when I try to run it locally I manage to tag the photo but I write the tag I get a pop up window saying "there was a problem with the API"
Will this problem be gone if I will uplod it to the internet?


If you are not running ColdFusion on your local computer, yes, you will get an API error (when the plugin tries to communicate with the server to save the tags).


That looks pretty good, thanks.

Hi Ben,

Your plugin is really neat and videos really help to understand how it's implemented, many thanks. Are you releasing the code under any sort of licence? I have a project where I need to give the admin users a way to tag objects in photos and your code would be a great help.

Thanks again,



Glad you like the videos. I typically release code as-is. If you like it, have at it. Of course, referrals back to this site always make me feel warm and fuzzy inside :)

Hi Ben. I love the plugin. However, I have been using it with Jquery UI Dialog, which I use to display images, and fire it up on the click event that show the dialog.

If you fire up the first image, it works, but when you click on subsequent images, the plugins is not fired up at all.

I know this because the _GET request is sent the first time you click on an image, but not on the others. You have to reload the page for it to work again, but it will still work on only one image.

Just to let you know about this issue. Thanks for the plugin! Great work!


It looks like that page needs a login; perhaps that's the problem? Is it possible the AJAX features are getting hit with a 401 Unauthorized response.

Ben -

Thanks for your work on this, and sharing it with us. I am integrating it into a client site and am noticing that I can't get the AJAX to 'POST' even though I set it as the default saveMethod. Everytime I save it's doing a GET with the params in the URL.

Any ideas?

We're trying to develop a proprietary member/group content tagging feature for WordPress and BuddyPress (ala Facebook Photos but for full-length embedded sports games). We have money for this project. Is it something you'd be interested learning about? Shoot me a message to learn more: chrism at

Chris McCoy, Founder

Well Good work.
Actually, i have a scanner which will scan my file and i need to upload it the folder and i have to tag the paragraphs of the file, my development is in Servlet and JSP.
Can anyone please help me using the above script. I need some guidance.Please

I have integrated your plugin in one of my sites. Now I am changing some code of it to fit my specific needs. We should support adding tags, removing tags, as well as additional operations like bring-up tag related content (as list or advertisement) and tag editing. I bind the operation with click event. However, I found it has conflict with dblclick(), only click() is captured when I try to remove the tags.

Is there any possible to bind the another events to the remove action? Like keypress() for d or D key, (DEL key is not valid)? Alternatively, maybe I should add a small delete object/icon on tag.

At last, I think adding too much event handlers to object might not be a good idea. Maybe I can just leave drag and click events, and leave any other operations to sidebar to operate, for editing, removing, displaying...


You should be able to bind other events such as key-up. The only challenge there would be coming up with a way to keep and maintain "focus" on a given tag such that the key-bound trigger would know which tag to act on.

Good luck - sounds like a cool little project.

Sorry for my English:-)

How can I reapply this projekt to div in photogallery where images are change with AJAX without reload page.

I dont know how do it. First image is ok and tags are ready, but another images cann't load tags and save tags.

Please help me.

Awesome blog/plugin Ben - outstanding as usual. I just wanted to share something I ran into with the community regarding delayed loading and the deactivateContainer() function.

I've been working to modify this to work with a project I'm involved in, but I ran into an issue when doing some load testing (using sleep() on the load page/cfc).

If is set isDelayedLoad to true, then I mouseout of the active container while the ajax request is still active (getting tags) it kills the population of the tags for that photo. I've been living your (fantastic) code for the past 48h so I see you have a handler for isUserCreatingTag() but not one to check if tags are queued/pending load. So my fix for this was pretty simple, I added: isTagsLoading = false; to the init and then flag it true prior to the load ajax request and then flip it back once the request has been completed.

Finally, I modified your watch for !hover to look for an obey this flag.

Final code:

  • this.container.hover(
  • function( event ){
  • // Activate the container.
  • self.activateContainer();
  • },
  • function( event ){
  • // If the user is currently drawing a tag, then we
  • // don't want to deactivate the container.
  • if (!self.isUserCreatingTag() && !self.isTagsLoading){
  • // There is no pending activity on the
  • // container, so deactivate it.
  • self.deactivateContainer();
  • }
  • }
  • );

So this will allow delayed loading of the tags and let the user poke around with other things on the page while we do our get.

Hope this all makes (semi)sense, and thanks again for this fantastic code.

Awesome stuff. I've been playing around with this and am wondering how hard it would be to hide the tooltip if the cursor it outside the bounds of the tag AND the message. Basically, I'm looking to allow people to click links in the message, but it disappears before you can click on one :-)

The reason that POST isn't working is that $.ajax uses "type" to set the method, not "method".

The only thing I think the plugin really lacks is a "destroy" method so that it can go away when you're done on a dynamic page.

Hi Ben,

The Photo Tagger is realy great. I created a small demo based on your tagger with JSF and RichFaces. It's realy nice.

I have one question. Is it possible to make the tagging work on an iPad?

i am getting a prompt that there was an problem with the API, i had downloaded the all the files and had modified the links everything works well but the plugin is unable to save the tags + everytime i try to tag a area it propmts the same problem in API msg, Do u have any solution for this error. Do reply.


Trying to integrate this plugin into express js


I'm new to AJAX and JQuery, but I tried just integrating your plugin to my site and every time I refresh the page, try to add a tag, or delete one, I get the alert that "there was a problem with the API".

Thanks a lot!