From our sponsor: Meco is a distraction-free space for reading and discovering newsletters, separate from the inbox.
In this tutorial we will create an image gallery with jQuery that shows a preview of each image as a little thumbnail. The idea is to hover over the slider dots and make the regarding thumbnail slide into the previewer. When clicking a slider dot, the full image will slide in from the right or left side, depending on the currently viewed image.
Update: If you are interested in integrating the thumbnails preview slider you might want to check out the new post on how to use only the preview part:
Thumbnails Preview Slider with jQuery
The beautiful images are by talented geishaboy500 and can be found here on his Flickr Photostream.
So, let’s roll!
Tiny break: 📬 Want to stay up to date with frontend and trends in web design? Subscribe and get our Collective newsletter twice a tweek.
The Markup
The HTML structure is going to consist of a main container which will have the image wrapper for the big image, the navigation items and the dot list with the thumbnail preview:
<div id="ps_container" class="ps_container"> <div class="ps_image_wrapper"> <!-- First initial image --> <img src="images/1.jpg" alt="" /> </div> <!-- Navigation items --> <div class="ps_next"></div> <div class="ps_prev"></div> <!-- Dot list with thumbnail preview --> <ul class="ps_nav"> <li class="selected"> <a href="images/1.jpg" rel="images/thumbs/1.jpg">Image 1</a> </li> <li> <a href="images/2.jpg" rel="images/thumbs/2.jpg">Image 2</a> </li> ... <li class="ps_preview"> <div class="ps_preview_wrapper"> <!-- Thumbnail comes here --> </div> <!-- Little triangle --> <span></span> </li> </ul> </div>
The thumbnail preview element will be a list item in the dot list. It’s going to have a special class since we want to treat this element differently. Each dot list item will contain a link element which will hold the information on the thumbnail image and the big image. Using JavaScript, we will extract that path information from the attributes and create the image elements dynamically.
Let’s take a look at the style.
The CSS
First, we will style the main container. Since our images have a maximum width of 680 pixel and a maximum height of 450 pixel, we will define the following values for the container (leaving some space for the dot list):
.ps_container{ display:none; width:680px; height:500px; margin:20px auto 0px auto; position:relative; }
Now we will style the wrapper for the full images. Here we really set the exact maximum dimensions and say that the overflow is hidden. We do that because we want to be able to put two images inside of this wrapper but cut off the overflow. In our JS function we will animate the images so that the current one gets revealed.
We will center the wrapper by setting the left and right margins to “auto”:
.ps_image_wrapper{ width:680px; height:450px; overflow:hidden; position:relative; margin:0 auto; -moz-box-shadow:0px 0px 5px #999; -webkit-box-shadow:0px 0px 5px #999; box-shadow:0px 0px 5px #999; }
The image(s) inside of the wrapper should be of position absolute since we want to animate the left value to slide in the current image and slide out the previous one:
.ps_image_wrapper img{ position:absolute; left:0px; top:0px; }
The navigation elements will have the following style:
.ps_prev, .ps_next{ width:30px; height:59px; position:absolute; top:50%; margin-top:-40px; cursor:pointer; opacity:0.5; } .ps_prev{ background:transparent url(../images/prev.png) no-repeat top center; left:-50px; } .ps_next{ background:transparent url(../images/next.png) no-repeat top center; right:-50px; } .ps_prev:hover, .ps_next:hover{ opacity:0.9; }
The dot list with the class “ps_nav” will be placed under the full image and centered by auto margins:
ul.ps_nav{ list-style:none; margin:0; padding:0; width:170px; margin:20px auto; position:relative; }
The dot list elements will float:
ul.ps_nav li{ float:left; }
And the inner link elements will get the dot background image, which is a sprites image:
ul.ps_nav li a{ display:block; text-indent:-9000px; width:11px; height:11px; outline:none; padding:0px 3px; background:transparent url(../images/dot.png) no-repeat top center; }
On hover we will change the background position to show the other half:
ul.ps_nav li a:hover,ul.ps_nav li.selected a{ background-position:50% -11px; }
Our special list element, the one that will have the thumbnail preview, will be of absolute positioning. The top has a negative value, since we want to pull this element up, beyond the list. The left value will be dynamically calculated. -34.5 pixel is the left value for the preview element when we want to show it over the first dot:
ul.ps_nav li.ps_preview{ display:none; width:85px; height:91px; top:-95px; left:-34.5px; /*This we will calculate dynamically*/ position:absolute; }
The span will be the little triangle:
ul.ps_nav li.ps_preview span{ background:transparent url(../images/triangle.png) no-repeat top center; width:15px; height:6px; position:absolute; top:85px; left:35px; }
The preview wrapper will function the same way like the full image wrapper. We will hide the overflow:
.ps_preview_wrapper{ width:75px; height:75px; border:5px solid #fff; overflow:hidden; position:relative; -moz-box-shadow:0px 0px 5px #999; -webkit-box-shadow:0px 0px 5px #999; box-shadow:0px 0px 5px #999; }
And ultimately, we want the thumbnails to be of absolute positioning since we want to animate the left value for the sliding effect:
.ps_preview_wrapper img{ position:absolute; top:0px; left:0px; }
And that’s all the style. Let add the jQuery spice!
The JavaScript
The idea of this gallery is to show little thumbnails when hovering over a dot that represents and image.
When moving the cursor over the dots, we want to create a sliding animation that moves the next currently hovered thumbnail image into place. This will create a great effect, giving the illusion that we have an invisible bar of thumbnail images above the dots and our preview element makes them visible.
We also want the clicked image to show up by “pushing” the current one away, either from the left or from the right side.
Both effects we will achieve by placing the images or thumbs next to each other and animating their left value accordingly.
So, let’s begin by caching some elements:
var $ps_container = $('#ps_container'), $ps_image_wrapper = $ps_container.find('.ps_image_wrapper'), $ps_next = $ps_container.find('.ps_next'), $ps_prev = $ps_container.find('.ps_prev'), $ps_nav = $ps_container.find('.ps_nav'), $tooltip = $ps_container.find('.ps_preview'), $ps_preview_wrapper = $tooltip.find('.ps_preview_wrapper'), $links = $ps_nav.children('li').not($tooltip), total_images = $links.length, currentHovered = -1, current = 0, $loader = $('#loader');
(The loader element was not mentioned in the HTML structure since we placed it outside of the container. We want to show a loading element until all the images are loaded. In the download file you will be able to see the preload function for the images.)
Now we need to check if the browser is a real one or, for whatever insane reason, a crappy one like, let’s say, IE:
var ie = false; if ($.browser.msie) { ie = true; // oh no sweet Jesus } if(!ie) // there is a God $tooltip.css({ opacity : 0 }).show();
Basically, we want to give the preview element or tooltip the opacity 0 and animate it to 1 when we hover over it. Since in IE it does not help to simply add an opacity filter (elements inside are still shown) we want to use the show/hide instead of animating the opacity. So, we add display:none to the style of the class but take it out if we don’t use IE.
After preloading the images, we will show the container:
/*first preload images (thumbs and large images)*/ var loaded = 0; $links.each(function(i){ var $link = $(this); $link.find('a').preload({ onComplete : function(){ ++loaded; if(loaded == total_images){ //all images preloaded, //show ps_container and initialize events $loader.hide(); $ps_container.show(); //when mouse enters the the dots, //show the tooltip, //when mouse leaves hide the tooltip, //clicking on one will display the respective image $links.bind('mouseenter',showTooltip) .bind('mouseleave',hideTooltip) .bind('click',showImage); //navigate through the images $ps_next.bind('click',nextImage); $ps_prev.bind('click',prevImage); } } }); });
The function showTooltip() will show the thumbnails preview and animate it to the right place. It will also slide the thumbnails inside, either to the right or to the left, depending where we are “coming from”:
function showTooltip(){ var $link = $(this), idx = $link.index(), linkOuterWidth = $link.outerWidth(), //this holds the left value for the next position //of the tooltip left = parseFloat(idx * linkOuterWidth) - $tooltip.width()/2 + linkOuterWidth/2, //the thumb image source $thumb = $link.find('a').attr('rel'), imageLeft; //if we are not hovering the current one if(currentHovered != idx){ //check if we will animate left->right or right->left if(currentHovered != -1){ if(currentHovered < idx){ imageLeft = 75; } else{ imageLeft = -75; } } currentHovered = idx; //the next thumb image to be shown in the tooltip var $newImage = $('').css('left','0px') .attr('src',$thumb); //if theres more than 1 image //(if we would move the mouse too fast it would probably happen) //then remove the oldest one (:last) if($ps_preview_wrapper.children().length > 1) $ps_preview_wrapper.children(':last').remove(); //prepend the new image $ps_preview_wrapper.prepend($newImage); var $tooltip_imgs = $ps_preview_wrapper.children(), tooltip_imgs_count = $tooltip_imgs.length; //if theres 2 images on the tooltip //animate the current one out, and the new one in if(tooltip_imgs_count > 1){ $tooltip_imgs.eq(tooltip_imgs_count-1) .stop() .animate({ left:-imageLeft+'px' },150,function(){ //remove the old one $(this).remove(); }); $tooltip_imgs.eq(0) .css('left',imageLeft + 'px') .stop() .animate({ left:'0px' },150); } } //if we are not using a "browser", we just show the tooltip, //otherwise we fade it in // if(ie) $tooltip.css('left',left + 'px').show(); else $tooltip.stop() .animate({ left : left + 'px', opacity : 1 },150); }
The function hideTooltip() simply fades out the thumbnails preview (or hides it if IE):
function hideTooltip(){ //hide / fade out the tooltip if(ie) $tooltip.hide(); else $tooltip.stop() .animate({ opacity : 0 },150); }
The following function will show an image in full size and animate the wrapper around to the right size. The new image will “slide into place”:
function showImage(e){ var $link = $(this), idx = $link.index(), $image = $link.find('a').attr('href'), $currentImage = $ps_image_wrapper.find('img'), currentImageWidth = $currentImage.width(); //if we click the current one return if(current == idx) return false; //add class selected to the current page / dot $links.eq(current).removeClass('selected'); $link.addClass('selected'); //the new image element var $newImage = $('').css('left',currentImageWidth + 'px') .attr('src',$image); //if the wrapper has more than one image, remove oldest if($ps_image_wrapper.children().length > 1) $ps_image_wrapper.children(':last').remove(); //prepend the new image $ps_image_wrapper.prepend($newImage); //the new image width //this will be the new width of the ps_image_wrapper var newImageWidth = $newImage.width(); //check animation direction if(current > idx){ $newImage.css('left',-newImageWidth + 'px'); currentImageWidth = -newImageWidth; } current = idx; //animate the new width of the ps_image_wrapper //(same like new image width) $ps_image_wrapper.stop().animate({ width : newImageWidth + 'px' },350); //animate the new image in $newImage.stop().animate({ left : '0px' },350); //animate the old image out $currentImage.stop().animate({ left : -currentImageWidth + 'px' },350); e.preventDefault(); }
The navigation functions will simply trigger a click event on the dots (which we already took care of in the beginning):
function nextImage(){ if(current < total_images){ $links.eq(current+1).trigger('click'); } } function prevImage(){ if(current > 0){ $links.eq(current-1).trigger('click'); } }
And that’s all! We hope you enjoyed the sweet thumbnails tutorial and find it useful!
ok … autoplay works fine with
setInterval( function(){
$(‘.ps_next’).click();
},5000 );
but when reach to last pic then stop, practically this should start from first again, anyone advice on this, please
@andry – the javascript is inside the demo html file. You can make it external if you want by using $(document).ready to execute it.
Hello @all,
1. Nice work Mary… 🙂
And my question comes as well to support us with a loop function. Please give a try on that feature.
Thanks in advance.
Cheers,
Dan
Here’s the loop function:
var gotoLoop = 0;
function nextImage(){
$links.eq(current+1).trigger(‘click’);
if (gotoLoop == 1) {
$links.eq(0).trigger(‘click’);
gotoLoop = 0;
}
if (current == total_images-1){
gotoLoop = 1;
}
}
Replace the nextImage() function with the code above!
Bye
Jo
Loop Forward, loop Back.
var gotoLoop = 0;
var backLoop = 1;
function nextImage(){
$links.eq(current+1).trigger(‘click’);
backLoop = 0;
if (gotoLoop == 1) {
$links.eq(0).trigger(‘click’);
gotoLoop = 0;
}
if (current == total_images-1){
gotoLoop = 1;
}
}
function prevImage(){
if(current > 0){
$links.eq(current-1).trigger(‘click’);
}
if (backLoop == 1) {
$links.eq(total_images-1).trigger(‘click’);
backLoop = 0;
}
if (current == 0) {
backLoop = 1;
}
}
I could not get this code to work even when i straight up cut and pasted your source code. I think the problem is that i have a PHP file instead of a html file…Is that relevent? Is their a work around for it?
1. @ giorgio: Thanks a lot my friend, is working like a charm.
2. @ harold: you´ll need to change the “(‘click’)” top-semicolons right before and after click. Write them with alt gr+#
@HAROLD RIGGSBEE
check the quotes
Thx!! this was very useful 😀
Hello Mary Lou,
Thankyou for the wonderful tutorial.
One question. I wish to make each image clickable so that when i click on each image it will take me to a certain linked page. Can you please tell me how i may accomplish this.
Look forward to hearing from you.
Thankyou
@ giorgio Thank you very much. it works for me.
Best I have seen, tested back to IE 6, FF3 – had my web partner test it and he gave the thumbs up on backwards compatibility.
Rejigged the CSS for IE issues but works GREAT!!!!
@Mary Lou – one question that I hope to modify. I would like to increase the number of dots – I figured it out in the the CSS but my dots are always to the left – can’t seem to get them centered – could you contact me?
@mustafa Hey Thanks for autoplay function 🙂 It was really very helpful
Hi. For Chrome I dont know how or why but Alban Message about line replacement work.
Hi,
could anybody help to add FancyBox on biger pic?
How can we add link on images?
@mustafa where we add code to autoplay
not work in chrome(but works on local server), and http://tympanus.net/Tutorials/SweetThumbnails/test/ also works in my chrome.How to fix it?
Tried to upload your source code – the problem is not resolved.
Please, help me!
I was wondering if it is possible to add captions that slide in with each image. Your help would be greatly appreciated.
Many thanks,
Alan
Hey, great post. Just I need one thing,
How can i put a “description, caption or title”, whatever, in each image, is posible?
Thanks
AMAZING! BRILLIANT! AWESOME!
Big thanks to Mustafa & Giorgio for helping me out with the autoplay and loop feature. Both work perfectly.
Still hoping someone has solved by issue of adding captions to each image. Any one have luck with this, if so please do kindly share with us.
Many thanks,
Alan
its good work Mary Lou thank you so much