View the Demo | Download the Zip

The screenshot above is jPhotoGrid in action. This plugin takes a simple list of images and captions and turns it into a grid of photos that can be explored and zoomed. You can check out a demo here.
Overview
Nearly all of the styling for this plugin is done in css. The trick is to layout the grid by floating the list items. The first thing the plugin will then do, is convert these all to absolutely positioned. This is what allows the plugin to zoom in on an individual image and then return it to its place. Like my interactive map plugin, this depends on the browsers ability to scale images. In my stylesheet, the images are set to width:100% and I simply animate the size of the container.
The Markup
The html for this plugin is simply an unordered list of images and captions. Here is the format:
<ul>
<li>
<img src="/hifi/site/edit/blog/source.jpg" alt="" />
<p>Caption Here</p>
</li>
...
</ul>
The CSS
The CSS is also fairly straight-forward. The key things to notice are the .active and .selected classes. When you hover over a list item, it is given the class 'active'. Once you have clicked the list item, it is given the 'selected' class.
#pg { position: relative; height: 585px; background: #000; }
#pg li { position: relative; list-style: none; width: 175px; height: 117px; overflow: hidden; float: left; z-index: 2; opacity: .3; }
#pg li.active { opacity: 1; }
#pg li.selected { opacity: 1; z-index: 99; -moz-box-shadow: 0px 0px 10px #fff; -webkit-box-shadow: 0px 0px 10px #fff; }
#pg li img { display: block; width: 100%; }
#pg li p { color: white; margin: 10px 0; font-size: 12px; }
The Javascript
The javascript is easy enough to set up. It needs to know the sizing of the thumbnails as well as the sizing and positioning of the zoomed image. This is how the plugin is able to zoom and restore each of the images. Note that you can also change the active and selected classes by setting them using the 'activeClass' and 'selectedClass' options.
$('#pg').jphotogrid({
baseCSS: {
width: '175px',
height: '117px',
padding: '0px'
},
selectedCSS: {
top: '50px',
left: '100px',
width: '500px',
height: '360px',
padding: '10px'
}
});
Conclusion
While not nearly as extensible or flexible as most of the plugins I like to put together, I enjoyed this one. It is a fun way to explore a gallery of images and ends up working great in conjunction with the jFlickerFeed Flickr Plugin I put together last week. If you have any questions or comments, leave them below.
View the Demo | Dowload the Zip
View the Demo | Download the Zip
We often work with clients that maintain accounts with Twitter, Flickr, Youtube and other services in addition to their website. Often they will want to pull in data from one of their accounts to their website. With Flickr, this is pretty easy because they make a simple API available. Having worked with it a few times, we decided to make it even easier to pull photos from a public feed.

There are a few examples of this in use on the demo page. All of the photos on this page have been used with the generous permission of John Roberts (@jroberts13).
Plugin Overview
This plugin works by pulling a JSON feed from Flickr and applying the data it gets back to a template. For example, we can generate this list of pictures:
<ul>
<li><img alt="Photo Title" src="http://farm4.static.flickr.com..." /></li>
<li><img alt="Second Photo Title" src="http://farm4.static.flickr.com..." /></li>
</ul>
From the following jQuery:
$('ul').jflickrfeed({
limit: 2,
qstrings: {
id: '44802888@N04'
},
itemTemplate: '<li><img alt="{{title}}" src="{{image_s}}" /></li>'
});
The plugin gets the feed from Flickr using AJAX and applies each image it gets back to the provided template.
The Plugin Options
There are a number of options available on the plugin, these are the defaults:
flickrbase: 'http://api.flickr.com/services/feeds/',
feedapi: 'photos_public.gne',
limit: 20,
qstrings: {
lang: 'en-us',
format: 'json',
jsoncallback: '?'
},
cleanDescription: true,
useTemplate: true,
itemTemplate: '',
itemCallback: function(){}
Here is a description of each one:
- flickrbase: This is unlikely to be needed. It is used to set up the url the jQuery AJAX call will need to be made to.
- feedapi: There are a number of feeds that flickr makes available. The default is the public feed. Here is the list of all that are available: http://www.flickr.com/services/feeds/. This has only been tested on feeds that return photos. So, for example, don't expect good results using this plugin on the Forum discussion feeds.
- limit: Set how many items you want to loop through. Flickr seems to limit its feeds to 20, so that is the default.
- qstrings: This is the most important setting. This is used to request the correct feed. In my examples I use this to set the user id. These are automatically added to the request url. Depending on which feed you use (http://www.flickr.com/services/feeds/) there are different sets of query parameters available: http://www.flickr.com/services/feeds/.
- cleanDescription: Flickr puts all kinds of junk in the description it returns. By default this plugin is set to remove everything but the plain photo description.
- useTemplate: Set this to false if you don't want to use the plugins templating system.
- itemTemplate: The template rules are described below.
- itemCallback: You can add a callback on each item. The scope is set to the container and the item object is made available.
Typically, the only things that will need to be set are limit, qstrings.id and itemTemplate.
Using the Templates
In order to make it really easy to use any kind of markup needed with this plugin, a simple templating system has been build in. Here is an example of a template:
<li>
<a href="{{image_b}}"><img src="{{image_s}}" alt="{{title}}" /></a>
</li>
You can see that this is just basic html with a few special tags mixed in. All of the tags are surrouned by double curly braces. The plugin works by putting in the correct information for each tag and each item into the template.
The tags that are available depend on what flickr returns for each item. For the Public Photos API these are: title, link, date_taken, description, published, author, author_id, tags, and image. For the image property, the plugin uses the Flickr URL scheme to make several sizes available. You can read about this at http://www.flickr.com/services/api/misc.urls.html. The image tags available are: image_s, image_t, image_m, image, image_b.
The Callback Parameter and Integration
The plugin's second parameter is a callback. This has the scope of the containing element and has the entire data response available. This is a great feature if you want to integrate this plugin with others. Let's say you want to integrate it with colorbox. If you call $('selector').colorbox() and then this plugin it won't work. This is becuase the plugin is adding elements to the page after the colorbox call. This is even true if you call colorbox after this plugin. Since this uses ajax, your images won't be added to the page until well after most of your javascript has run. (In computer time).
Instead, the trick is to use the callback function. This allows you to pass in code that you want to run after the images have loaded. So for example, you can do this:
$('#cbox').jflickrfeed({
limit: 14,
qstrings: {
id: '37304598@N02'
},
itemTemplate: '...example...'
}, function(data) {
$('#cbox a').colorbox();
});
Now the colorbox call won't be made on the photos until they are loaded.
Demo and Download
You can see a demo of this plugin on the demo page. Additionally you can download a zip of this plugin and the example.
As always, if you have any questions or comments, leave them below. Enjoy!
Update 1/22/2009 - This plugin now contains support for an active state. It is just as easy to use and all of the animation still works with it. Read below to see how it works. (This was originally posted 10/28/2009)

Download the Files (Zip) | View a Demo
We have written and released a jquery dropdown menu plugin as well as a CSS Sprites2 Plugin -- this post is along the same lines. Its purpose is to allow you to build an image-based menu with animated hover states as easily as possible and by using the most concise descriptions possible. To see the results on both a horizontal and vertical menu, check out the demo.
Setting up the jQuery Menu Plugin
The first component when doing something with sprites is a combined image that contains all menu states. For the menu above, this is the image below was used. (This was designed by Liaison Design Group, one of our Partners)

The image contains, the normal state, the hover state and the active state. The value to doing things this way is that it allows your site to load faster. Rather than downloading an image for each nav item its hover state and its active state, only a single image needs to be downloaded. This minimizes the overhead of many http requests.
The next thing to do, is set up the HTML for the nav bar:
Then we need to set up the CSS. There are a couple of things to note here. We are applying the background image to the containing element so that we don't need to respecify background positioning. This also makes the menu usable if javascript is disabled. Each element needs to have its size defined specifically as well.
#hnav { position: absolute; top: 0; left: 0; width: 615px; height: 72px; background: url('horiz_sprites.gif') no-repeat; }
#hnav li { position: absolute; left: 0; height: 72px; }
#hnav #hnavhome { width: 82px; left: 0px; }
#hnav #hnavlocal { width: 146px; left: 82px; }
#hnav #hnavhigher{ width: 162px; left: 228px; }
#hnav #hnavcomm { width: 143px; left: 390px; }
#hnav #hnavnews { width: 82px; left: 533px; }
#hnav li a { display: block; position: absolute; top: 0; left: 0; width: 100%; height: 72px; text-indent: -9999em; }
Notice how much less complicated the CSS is than what is typical with sprites. There is no need to define background positioning for each element and its hover state. The last piece you'll need to do is enable the autosprites plugin:
$(document).ready(function(){
$('#hnav').autosprites();
});
There are no required options. The plugin defaults to a horizontal menu and fading for the animation. It infers everything else from the CSS. If you would like to customize things, here are the options that are available:
settings = $.extend({
offset: '100%',
orientation: 'horizontal',
over: { opacity: 'show' },
overSpeed: 500,
out: { opacity: 'hide' },
outSpeed: 500,
activeState: false,
activeClass: 'active',
activeSprites: false
}, settings);
The only bit worth explaining is the active state. In the image above, I show three states. By default, you only need two, a normal state and a hover state. If you set 'activeState' to true, it will use the hover state by default. If you want to specify your own active state, simply set 'activeSprites' to true as well.
So that's it! You can specify the minimum amount of information about your menu and the sprites will be built automatically. Be sure to check out the demo and download the zip for your own projects. As always leave a comment if you have any complements, insults, suggestions or questions.
A Special Note on Compiling Javascript
The purpose of using sprites is to minimize HTTP requests. It would be foolish to use this plugin by including jQuery, the minimized plugin, and a setup script. Instead, it is best practice on a production site to bring all of your scripts together into one file, just as sprites bring your images into one file.
This is the second of two posts about a
site we recently launched for a non-profit called Striving for More. This is a great new organization that has started with the goal of improving overall care for young cancer patients. It was a fun and deserving project with a great design:

The first post was about how to build a custom jQuery Slideshow on top of some popular jQuery plugins. It covered the required html, css and js needed and it also gave a look into the through process that went into it. This post will take the same approach, but instead look at the custom jQuery horizontal accordion that is on the homepage. Unlike the last project, this one will be build without the help of any plugins. It was created entirely from scratch using jQuery custom events.
Here is a look at the final product.

What is it?
Essentially, this is a horizontal accordion that also changes the text below it on each transition. The one difference between this and a traditional accordion is that when a slide becomes active or inactive, it doesn't just shrink. Instead it slides behind the other slides. Click around on it above to see what I mean.
The HTML
The html for this project is really straightforward. There is a group of slides and a group of content:
<div id="slideshow">
<ol id="slides">
<li class="slide open" id="slide-[SLIDE NUMBER]">
<a href="[SLIDE LINK]">
<img src="[SLIDE IMAGE]" alt="" />
</a>
<a class="slidebutton" href="javascript:void(0);"><img src="[SLIDE BUTTON]" alt="" /></a>
</li>
...
</ol>
<ol id="slidecontents">
<li class="slidecontent" id="content-slide-[SLIDE NUMBER]">
[SLIDE CONTENT]
</li>
...
</ol>
</div>
The html is in two logical groups, the slide images/navigation and the slide content. These are paired by convention using programatic id attributes. Notice that each of the image slides and the content slides has an id with the [SLIDE NUMBER] in it. This makes the javascript much simpler.
Ideally the content would be grouped in with the images. This would have made for better markup, as the related items would be put together. This would have required a lot of css positioning trickery since the content wasn't supposed to move with the slide. For the sake of overall simplicity, some html complexity was added.
The last thing worth noting here is that the slide button text is actually a rendered image. That text can't be rotated reliably in all browsers is really sad. I am confident that this will be accomplished shortly with Canvas. If you're wondering which browsers are the offenders here, you'll probably be surprised. Text can be rotated reliably in EVERY major browser except for Firefox 2.0. It even works in IE. Alas.
The CSS
The CSS for this project is probably the simplest part. You'll only notice one little quirk:
#slides { position: absolute; top: 21px; left: 22px; width: 577px; height: 285px; overflow: hidden; }
#slides .slide { position: absolute; top: 0px; width: 541px; }
#slides .slide img { position: absolute; top: 0px; left: 0px; }
#slides .slide .slidebutton { display: block; position: absolute; top: 0px; right: 0px; height: 285px; width: 21px; background: #693d5e; text-decoration: none; border-right: 1px solid white; }
#slides .active .slidebutton { background: #55354a; }
#slides .slide .slidebutton img { position: absolute; top: auto; display: block; bottom: 5px; left: 5px; }
/* Manually place slides to begin */
#slide-1 { position: absolute; top: 0; right: 44px; z-index: 3; }
#slide-2 { position: absolute; top: 0; right: 22px; z-index: 2; }
#slide-3 { position: absolute; top: 0; right: 0px; z-index: 1; }
It's all straighforward except for that bit at the bottom. I've hardcoded the number of slides. This is typically something I like to avoid, but there were a couple of reasons for it on this project:
- I was assured that these slides would be unchanged for quite some time.
- At least some CSS Positioning would be required so the page wouldn't look broken while the JS loaded. By being exact there would be no jarring readjustment at all.
- It was easier.
These reasons made the decision obvious. As a quick aside and bonus, here is the css that could be used to rotate the text in CSS. Note that this depends on slightly different markup and it just won't work in Firefox 2.0. So remeber that until FFox2.0 disappears this is useless, though fascinating.
#slides .slide .slidebutton span
{ display: block; text-align: right; -webkit-transform: rotate(90deg); -moz-transform: rotate(90deg);
position: absolute; bottom: 100px; left: -90px; width: 200px;
color: #f2ecd0; font-family: verdana, sans-serif; font-size: 12px; }
/* Put the following in your IE stylesheet */
#slides .slide .slidebutton span { bottom: 190px; left:2px; filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); }
The jQuery
The jQuery for this little project was the most fun. It ended up being very compact due to the intial positioning of the slides using CSS and because I used custom events.
What are custom events? In short, they are functions that you can bind to jQuery selectors. This lets you associate actions with the objects that do them, rather than with what triggers them. As an example, you could bind the event 'toggle' to a lightbulb and then trigger it from any number of switches. So if you have multiple switches, they all don't need to keep track of the lightbulb's state. They can just call toggle and the bulb will handle it. (If you like this example, don't credit me. It's borrowed from a great article on jQuery Custom Events written by Rebecca Murphey).
So here is the entirety of the jQuery code:
/* Binding Events to the slides */
$('.slide')
.bind('open', function(){
if(! $(this).hasClass('open')){
$(this).next().trigger('open');
$(this).addClass('open');
$(this).animate({right: "-=511px"});
}
else{
$(this).prev().trigger('close');
}
$(this).siblings().removeClass('active');
$(this).addClass('active');
})
.bind('close', function(){
if($(this).hasClass('open')){
$(this).removeClass('open');
$(this).animate({right: "+=511px"});
$(this).prev().trigger('close');
}
});
/* Binding Events to the Slide Contents */
$('.slidecontent').bind('show', function(){
$('.slidecontent').removeClass('open');
$(this).addClass('open');
});
/* Triggering from the buttons */
$('.slidebutton').click(function(){
$(this).parent().trigger('open');
$('#content-' + $(this).parent().attr('id')).trigger('show');
});
This is certainly terse, so it is worth explaining. I'll start with the events we are binding to '.slide', open and close. To set the stage, remember that each slide is either open (navigation on the right), or closed (navigation on the left). I am keeping track of this by applying an 'open' or 'closed' class to each slide. This is a fairly standard technique.
Where things might get a bit tricky lie within how an accordion works. The visibile slide isn't the only slide that is open. The slides that come after it must be open as well (their navigation is on the right). So when the second slide is active, the first must be closed and the second and third must be open. This problem gets handled with a little big of recursion.
The Event Binding
Let's look at the close event, as it is the simpler of the two. When we close a slide, we first check that it is open. If it is closed, there is nothing to do. If it is open, we close it and then close the slide immediately to its left. This repeats until there isn't a slide to the left or it reaches a slide that has already been closed.
The open event is only a little more complicated. It works in the exact same way as the close event, except trickles to the right. It also updates which slide is active at each stop along the way.
Finally you can see how the 'show' event is bound to the slide contents. All this does is remove an open class from every slide content, and then add it back to the one that has been triggered.
As you can see, a lot of complexity has been encapsulated under these events. This makes it really easy to jump to a particular slide. All that needs to happen is to trigger the 'open' event on the desired slide and the 'show' event on the correct content. This is exactly what the '.slidebutton' code does.
Putting it all together
With all of the pieces in place you can see it in action below. Be sure to leave any questions or comments you have about this in our comments below.

Inayaili de León's post last week on 24 ways reminded me about some of the new attributes that HTML5 will be adding to the <input> element. In addition to the flashy new input types (search, color, date, etc.), there are several more mundane, but just as useful, new attributes that can be used on the input types we already have. I created a small jQuery plugin that checks if these attributes are supported natively, and if not it adds support via JavaScript. See an example, or continue reading for the full explanation.
Placeholder
A common trick is to set the value of a text input to a short hint about the type of information that should be entered there. Then a couple lines of JavaScript are used to clear that value when the user clicks or tabs into the input. Although there are usability issues with this technique, its convenience is hard to resist. The HTML5 spec includes a new placeholder attribute that creates the same effect without the usability problems.
To Use:
<input type="text" name="name" id="name" placeholder="Enter your first and last names" />
Autofocus
When the primary purpose of a page is the form—a login page, for instance—it is helpful to start out with the focus on the first input, so the user can just start typing. You can use the new autofocus attribute on the first element to do that. (Be careful, though: setting autofocus on a non-essential form, like a comment form, can be very annoying.)
To Use:
<input type="text" name="name" id="name" autofocus="autofocus" />
Required
Setting the required attribute prevents the form from submitting unless the input has some value.
To Use:
<input type="text" name="name" id="name" required="required" title="This is required" />
Pattern
The pattern attribute lets you set a regular expression pattern that must be matched by the input value before the form can be submitted. This is a very powerful tool, allowing for complex validations. Our HiFi RexExp Tool will help you create regular expressions that test for anything from credit card numbers to urls. You should also read the HTML5 spec on the topic, as there are a few caveats. In particular, be aware that the pattern matches the entire value by default, and you should not wrap the expression in forward slashes.
To Use:
<!-- Only accept US zip codes -->
<input type="text" name="zip" id="zip" pattern="(^d{5}$)|(^d{5}-d{4}$)" title="US zip codes only" />
Using the Plugin
Some or all of these new attributes are supported by both Safari and Opera. But Firefox and Internet Explorer do not support them natively at this time. To be safe, the nmcFormHelper plugin checks for availability of each attribute (using a technique suggested by Mark Pilgrim) and, if it doesn't exist, uses JavaScript to emulate support.
At a minumum, you just need to include my script and place a single line in your $(document).ready() block: nmcFormHelper.init();. That will set up all of the new attributes, and you're ready to go.
Options
- Placeholder styling: The placeholders are styled to match the native placeholder functionality in Safari. If you wish to set a different style, you can set
nmcFormHelper.placeholder.styling = {'color':'red','font-style':'italic'}; or whatever look you choose.
- Validation functions: The
required and pattern attributes will prevent form submission and display an error if the value does not exist or does not match the pattern. By default, a new label will be added after each invalid input, containing the title text of the input. But you can write a function to do anything you want! Just override the functions nmcFormHelper.validation.showErrors and nmcFormHelper.validation.hideErrors (the latter function should undo the former). Both functions will be passed a jQuery object containing all the elements that are affected. Each element will also have one or both of the classes "requiredError" or "patternError" set.
Bonus!
Since Internet Explorer does not support CSS attribute selectors, it can be hard to style a text input, for instance, without screwing up your buttons, or vice-versa. So the plugin also adds a class to each of your inputs containing "input-" and the type of the input (i.e. "input-submit"). That way, you can style them individually.
Get the Code
Full, commented version
Minified version
Since I released my nmcDropDown plugin for jQuery two weeks ago, several people have been asking for a simple example of how to use it. Althought the plugin takes care of all the behavior automatically, you still need to style and format the menu using CSS. In this post I will demonstrate two simple menu styles that use nmcDropDown.
The bare minimum
The examples below have styling and effects to make them look nice, but first I will show you the minimum you need to get your menus running. First off, you need a set of nested lists and link to create the structure of your menus:
<ul id="nav">
<li><a href="#">Item One</a>
<ul>
<li><a href="#">Sub-menu Item 1</a></li>
<li><a href="#">Sub-menu Item 2</a></li>
<li><a href="#">Sub-menu Item 3</a></li>
</ul>
</li>
<li><a href="#">Item Two</a>
<ul>
<li><a href="#">Sub-menu Item 1</a></li>
<li><a href="#">Sub-menu Item 2</a></li>
<li><a href="#">Sub-menu Item 3</a></li>
</ul>
</li>
<li><a href="#">Item Three</a>
<ul>
<li><a href="#">Sub-menu Item 1</a></li>
<li><a href="#">Sub-menu Item 2</a></li>
<li><a href="#">Sub-menu Item 3</a></li>
</ul>
</li>
</ul>
Now you need a bit of CSS to put everything in its correct place. This menu isn't going to look nice, but it will give you a starting point from which you can match your site's overall aesthetic.
#nav { float: right; height: 30px; }
#nav li { float: left; position: relative; }
#nav li a { display: block; padding: 5px 10px; line-height: 20px; }
#nav li ul { display: none; position: absolute; top: 30px; left: 0; width: 120px; background: #fff; }
#nav li:hover ul { display: block; }
#nav li ul li { float: none; }
#nav li ul li a { display: inline-block; }
#nav li ul li a { display: block; }
The most important thing here is to set #nav li to position: relative; and #nav li ul to position: absolute; to the submenus are aligned with their parent item. In the interest of accessibility, we are also hiding the submenus in the CSS (#nav li ul { display: none; }) and showing them with their parent li is hovered over (#nav li:hover ul { display: block; }. That way, if JavaScript is disabled, the menus will still work in every browser but Internet Explorer 6. JavaScript is always required for drop-downs to work in IE6, unfortunately. Speaking of which, did you notice the oddity at the end where we declare the links as display: inline-block and then re-declare them at display: block? That is the only concession we need to make to Internet Explorer bugs—it removes the extra space IE6 inserts between list items, as discovered by Roger Johansson.
This CSS will create a horizontal menu with drop-downs, but by changing a few lines, you could just as easily make it a vertical menu with fly-outs.
Finally, the JavaScript to hook up the nmcDropDown script. Make sure you also paste the nmcDropDown script itself (and the HoverIntent plugin, if you wish to use it) into the JavaScript file.
$('#nav').nmcDropDown();
Makin' it Pretty

Now that we have a functioning menu, we can start styling it. I have created an sample page with two different examples using nmcDropDown.
The first—in the top-right of the page—is based on the simplified example above, just with additional CSS to style the menu bar and drop-downs. I also added an additional parameter to the call in JavaScript to make the menus slide down instead of fading in: $('#nav').nmcDropDown({show: {height: 'show', opacity: 'show'}});.
The second example is a little more interesting, as it's actually not a menu at all. In the right-hand sidebar, I am using nmcDropDown to create an informational panel with four heading that, which clicked on, each reveal a bit of text. To do this, I replaced the second level of <ul>s in my HTML with paragraphs. I then used the following CSS to line arrange everything vertically:
#sidebarNav { padding: 10px 0; background: #ccc; border: 1px solid #bbb; -webkit-border-radius: 4px; -moz-border-radius: 4px; border-radius: 4px; }
#sidebarNav li { border-top: 0 solid #ccc; }
#sidebarNav li:hover, #sidebarNav li.open { background: #bbb; }
#sidebarNav li a { display: block; padding: 5px 10px; line-height: 20px; color: #444; font-weight: bold; text-decoration: none; }
#sidebarNav li p { display: none; padding: 5px 10px 10px; color: #444; border-top: 1px dashed #aaa; }
#sidebarNav li:hover p { display: block; }
Finally, in the JavaScript I told nmcDropDown to activate on click rather than hover and to look for my paragraph instead of the unordered-list it usually expects:
$('#sidebarNav').nmcDropDown({
trigger: 'click',
submenu_selector: 'p',
show: {height: 'show'},
hide: {height: 'hide'}
});
Please look at the sample page to try these out, and view source to examine the code in more detail. If you find another creative way to use nmcDropDown, please link to it in the comments.
Orginally Posted March 2, 2009 | Last Updated November 4, 2009
jCaption is a jQuery plugin designed to make adding captions to a page dead simple. It takes an image element and uses one of its attributes to build the markup for a caption. It allows for both arbitrary styling and markup.

Dealing with image captions on the web can be a big pain. The code structure for nearly any caption looks something like the following:
<div>
<img src="image.gif" alt="" />
<p>Caption Text</p>
</div>
While writing such markup for every image is merely an inconvenience for web developers, it is an impossibility for most of thier clients. Many clients use a web-based WYSIWYG editor to create and edit their pages through a CMS. It would be nearly impossible to expect them to add this markup around their images.
This was a problem I faced when building a site for a client. I found well-concepted solution in Captify, but I found it too restrive for my needs. I proceeded by writing a version that met the following requirements:
- Follow jQuery Conventions - Animations shouldn't be handled by passing in a string, instead arbitrary animations should be allowed.
- Markup Should be Arbitrary - Other than the
tag that is required, other markup should be arbitrary.
- Optionally Allow Placement From Text-Editors - Web-Editors like tinyMCE or FCKditor allow users to align images to the left or right of text. Depending on the version, this is accomplished using the "align" attribute or "style" attribute. This plugin accomodates both and makes sure that the caption is correctly placed as well.
Using the plugin
Using the plugin is dead simple. Just use apply the .jcaption() method to the images you want:
$('img').jcaption();
By default this will take the following image:
<img src="image.jpg" alt="Some caption text" />
and produce the following markup:
<div class="caption">
<img src="image.jpg" alt="Some caption text" />
<p>Some caption text</p>
</div>
The options will allow you to control the elements and classes that the plugin produces. Additionally, to accomodate text editors, the plugin can copy the styling that was applied to the image to the caption instead, and take the images "align" attribute and append it to the caption class. So for text editors that place images using align="left" you can instead have the plugin add "left" as a class to the caption so that you can place the entire caption using your stylesheet.
Options
$('img').jcaption({
//Element to wrap the image and caption in
wrapperElement: 'div',
//Class for wrapper element
wrapperClass: 'caption',
//Caption Element
captionElement: 'p',
//Attribute of image to use as caption source
imageAttr: 'alt',
//If true, it checks to make sure there is caption copy before running on each image
requireText: true,
//Should inline style be copied from img element to wrapper
copyStyle: false,
//Strip inline style from image
removeStyle: true,
//Strip align attribute from image
removeAlign: true,
//Assign the value of the image's align attribute as a class to the wrapper
copyAlignmentToClass: false,
//Assign the value of the image's float value as a class to the wrapper
copyFloatToClass: true,
//Assign a width to the wrapper that matches the image
autoWidth: true,
//Animate on hover over the image
animate: false,
//Show Animation
show: {opacity: 'show'},
showDuration: 200,
//Hide Animation
hide: {opacity: 'hide'},
hideDuration: 200
});
You'll see that any arbitrary animations are possible by setting up the options in this manner.
Demo

View a Demo of this plugin with and without animations here. The captions on the left are animated and the caption on the right is not.
Download the plugin
This plugin has been tested in IE 6 and 7, Firefox 2 and 3, Safari and Chrome.
Updated Sept. 2, 2009 - Added CopyFloattoClass, improved some documentation and changed defaults to reflect the current state of text editors.
Updated Nov. 4, 2009 - Added requireText, allowing the plugin to be run even if there is no caption text.
Download a zip of the plugin and the sample.
Update: Since this post was written, we have rebuilt the New Media Campaigns website. There still is a filtering portfolio, but it is now based on the jQuery isotope plugin. This is a much better plugin however it does cost $40 for commercial use. The plugin below still works great, but it doesn't animate quite as nicely.
See a Demo
Read The Documentation
Download the Plugin and Zip

Last week we finally launched our new site design. One of our favorite new features is our filtering portfolio. We've selected 50 of the websites we've done to be featured and we've categorized each one. Using jQuery, it is possible to filter through the big list to see sites that apply to certain categories. Feel free to test it out and sort through it here.
Some of the requirements we had when we built it were:
- It should be easy to update
- Each item should be able to be in multiple categories
- We should be able to link to certain filters with a url hash
To get this done, Josh, Eli and I collaborated on different aspects. We've finally finished it up and wrapped it up in a plugin that can be used by anyone. To download a zip with the plugin, see the bottom of this post.
Documentation
Markup
The plugin is configurable to work with just about any markup. Here is an example of what we used for the filters and the portfolio items.
Filters
Portfolio Items
-
Saranac
...
Of course the markup can be completely arbitrary. All of the styling is done in css. There are only two major requirements:
- Linking Filters to Items - The hashed href of the filter link must match the class of the portfolio item. In the example above, the #jquery filter matches the last class of the only portfolio item.
- Portfolio Items are Wrapped - As you'll see in the JS section below, the plugin is called on a wrapper of the portfolio items. In this case, the wrapper is the
Javascript/jQuery Setup
The jQuery is really simple to set up. If you use the same markup as I have, you can even leave out the settings and it should work out of the box. Simply do the following after you have included the filterable plugin:
$(document).ready(function(){
$('portfolio-list').filterable();
});
The plugin will also take a number of optional parameters. These are the defaults:
settings = $.extend({
useHash: true,
animationSpeed: 1000,
show: { width: 'show', opacity: 'show' },
hide: { width: 'hide', opacity: 'hide' },
useTags: true,
tagSelector: '#portfolio-filter a',
selectedTagClass: 'current',
allTag: 'all'
}, settings);
To change any of the defaults, just pass them in to the initial call:
$(document).ready(function(){
$('portfolio-list').filterable({
animationSpeed: 2000,
show: { height: 'show' }
useTags: false,
etc...
});
});
Fun Features
Adding a Hash to a Url
If you have the useHash setting enabled, you can link directly to a single filter. You can see this in effect on our portfolio by visiting http://www.newmediacampaigns.com/section/portfolio#jquery.
Exposed Events
Once filterable() has been called, there are a number of events that get bound to the wrapper. Here they are:
/* Handles the state of the filter buttons as well as the portfolio items
* Expects "tagToShow" to include a hash. */
$(this).bind("filter", function( e, tagToShow ){});
/* Just switches the portfolio items. Expects a class name */
$(this).bind("filterportfolio", function( e, classToShow ){});
/* Shows a tag in addition to those being shown - expects a selector */
$(this).bind("show", function( e, selectorToShow ){});
/* Hides a tag - expects a selector */
$(this).bind("hide", function( e, selectorToHide ){});
The most useful of these is the first. It will allow you to programatically change the state of the filters and portfolios. So lets say you have a link somewhere on a page that you want to use to make sure your jQuery tag is shown. You could code something like the following:
$(document).ready(function(){
$('portfolio-list').filterable();
$('#linkID').click(function(){
$('portfolio-list').trigger('filter', [ '#jquery' ]);
});
});
For more details about these exposed events, feel free to check out the source or ask a question in the comments. If you're ready to test it out, just check out the zip below.
Download the Zip
To download this plugin and a sample project, click here.
To see the sample project first, click here.
What is it and why was it built?
A couple of months ago we launched a site about Marine Science in North Carolina with one of our design partners, Liaison Design Group. A key part of the project was an interactive map that allowed visitors to find important Marine Science resources in North Carolina.
Each location on the map would be represented by a bullet. Clicking the bullet would bring up more information on the location. Since the locations of the bullets tended to be highly clustered, zooming into select subregions was possible.
We wanted the experience to be engaging as possible but also easily updatable in the future. We settled on jQuery as the interface technology to use as it made it simple to build, display and animate the map. I did a longer writeup on the detailed mechanics of how this worked two weeks ago in one of our most popular posts. Due to the great feedback we got on it, this is a follow-up with a revised version of the code as a jQuery plugin which should make it easier to integrate into other projects.
Demos
This project is being used in production on a number of sites. Below is a listing of a couple. If you have used this in a project you would like to see listed, let me know.
Documentation
Limitations
This was originally developed for one project with very specific requirements. While I have taken the time to generalize that code into a plugin, I have not made it as generic as I would have liked and will do more in the future to improve it given enough interest. Here is a list of its current biggest limitations:
- Data Required In Plugin Setup - Ideally all of the required data would be stored in the html pages that drive the bullets. This would allow a CMS to more easily generate additional depth levels without needing to play with the javascript. All that is required in the js now is an array of the sub-regions along with their dimensioning data.
- Data Accessibility Unaddressed - The plugin currently does nothing on its own to make content available to search engines and those with disabilities. This is left up to the developer to do, though it shouldn't be too challenging since the plugin requires all of the data to be expressed in html.
- API - Right now it is impossible to programatically interact with the map once it is launched. Eventually it will have a simple API to assist in navigation and other manipulations.
Even with these limitations, this is ready for production and is being used on several sites as seen above in the demos.
Instructions
There are four main components required to make the plugin run: the background images, links to pages that contain html data in the correct format, some CSS for style and finally the plugin call. Below are instructions on each.
1. Background Images
There are very limited requirements on the background images:
- They must all be the same size. Sub-region background maps grow to fill the entire map area
- They should line up. Just prior to zooming, a sub-region map is placed over an area of the main map, if this doesn't line up properly, some of the zooms effect will be lost.
- The main map should highlight the zoomable regions. The plugin does not otherwise make these obvious. (Though the css could be edited to add a border or background image to the zoomable region.)
2. HTML Data
The plugin assumes a certain structure for the html data it loads via AJAX. As long as the basic structure is kept, a developer is free to add any type of content styled however they would like. Aside from the unavoidable requirements imposed by the animation, all style is isolated to CSS.
The HTML format contains information for the location and applied class of the bullets, a convention based link between the bullets and the pop-up content and the content itself. The html returned should just be a listing of the bullets and popups in the following format:
[POPUP-ID] should be unique for each bullet/popup. The jQuery uses this along with the "-box" convention to open the correct popup when a bullet is clicked.
3. CSS Style
As much as possible, the plugin is designed to operate exclusively on the DOM, leaving styling up to CSS. A basic css file is included with the demo zip. Here is some rough minimal css:
#map { position: relative; width: 700px; height: 470px; overflow: hidden; }
#returnlink { display: block; position: absolute; bottom: 0; right: 0; }
#map a.bullet { display: block; position: absolute; width: 10px; height: 10px; background: yellow; }
#map img.zoomable { }
#map div.popup{ display: none; position: absolute; width: 200px; height: 300px; }
#map div.popup a.close{ display: block; position: absolute; bottom: 0; right: 0; }
The code above will work just fine as a starting point. Obviously a lot of embellishment can be added to make the map look as good as the one's Liaison designed in the examples. Here are some things to note:
- The main '#map' container must be positioned absolute or relative.
- Popups must have 'display: none' set to make sure they don't flicker when they are loaded.
- Popups must be positioned using CSS. The jQuery only shows/hides them.
4. jQuery Plugin Call
The last piece that needs to be put in place is a call to the jQuery zoommap plugin that makes it all happen. In the call, we must pass the in some settings as well as a data structure that sets up the map and its different zoom levels. Here is a call for a simple two level map that contains all of the possible settings:
$('#map').zoommap({
// Width and Height of the Map
width: '500px',
height: '580px',
//Misc Settings
blankImage: 'images/blank.gif',
zoomDuration: 1000,
bulletWidthOffset: '10px',
bulletHeightOffset: '10px',
//ids and classes
zoomClass: 'zoomable',
popupSelector: 'div.popup',
popupCloseSelector: 'a.close',
//Return to Parent Map Link
showReturnLink: true,
returnId: 'returnlink',
returnText: 'return to previous map',
//Initial map to be shown
map: {
id: 'campus',
image: 'images/campus.jpg',
data: 'popups/campus.html',
maps: [
{
id: 'quads',
parent: 'campus',
image: 'images/quads.png',
data: 'popups/quads.html',
width: '200px',
height: '232px',
top: '18px',
left: '176px'
}
]
}
});
Here is a breakdown/description of each of the settings:
- width/height - The width and height of the map container.
- blankImage - Path to an (transparent) image that will be used over the zoomable regions until their map is loaded.
- zoomDuration - Duration in milliseconds of the map zoom effect.
- bullet(Width/Height)Offset - Offsets that allow the coordinates of the bullets to be placed in the center of the target, rather than the corner.
- zoomClass - Class that should be applied to the zoomable region imgs.
- popupSelector - Selector the plugin should use to find the popups in the html
- showReturnLink - Whether a return link should be shown on child maps
- returnId - Id to use for the return link.
- returnText - Text to be placed in the return link.
- map - The top level map that should be shown.
Additionally each map has the following properties and can infinately nest more maps:
- id - Id that should be used for the zoomable region.
- image - Path to the image that should be used for the map. This image should be large enough to fill the entire map space.
- data - Path to the html data for the maps popups and bullets.
- width/height - Width and height of the zoomable region.
- top/left - Absolute position of the zoomable region
- maps - Children maps.
Here is a minimal call that relies on the default settings:
$('#map').zoommap({
width: '500px',
height: '580px',
blankImage: 'images/blank.gif',
map: {
// Map structure
}
});
And here are the default settings:
settings = $.extend({
zoomDuration: 1000,
zoomClass: 'zoomable',
popupSelector: 'div.popup',
popupCloseSelector: 'a.close',
bulletWidthOffset: '10px',
bulletHeightOffset: '10px',
showReturnLink: true,
returnId: 'returnlink',
returnText: 'Return to Previous Map'
}, settings);
Download the Project Zip
View the project Demo or download the project zip.
Questions/Comments?
This project will be revised in the future and is certainly open to suggestions. If there are any suggestions or questions, please leave them in the comments.
Update (8/2/2011): Daniel Madejak found this plugin and liked it so much that he wrote a Windows desktop application that generates the config files for you! You can check out that application along with the documentation here: http://shaimad.pl/webmapcreator/. Note that it is an early release and improvements are still being made.