Sweet Thumbnails Preview Gallery

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 […]

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!

Manoela Ilic

Manoela is the main tinkerer at Codrops. With a background in coding and passion for all things design, she creates web experiments and keeps frontend professionals informed about the latest trends.

Stay in the loop: Get your dose of frontend twice a week

👾 Hey! Looking for the latest in frontend? Twice a week, we'll deliver the freshest frontend news, website inspo, cool code demos, videos and UI animations right to your inbox.

Zero fluff, all quality, to make your Mondays and Thursdays more creative!

Feedback 128

Comments are closed.
  1. the script stops showing pictures when you are on the last one. there’s a way to keep it going on repeating from the first one? BTW this script is MAGIC!!!

  2. In order to add more images you have to modify the width in the css
    ul.ps_nav{
    list-style:none;
    margin:0;
    padding:0;
    width:300px;
    margin:20px auto;
    position:relative;
    }
    I have something like that and i’ve added 15 images:D

  3. HI there,

    This is a beautiful script and I love it. I downloaded it and it works locally, but as soon as I change the path to the thumbnails and the images it doesnt seem to work 🙁

  4. i am going to make a theme can i use it for commercial purpose, i will use this script as a homepage slider

    Please reply soon

  5. Please help me to add caption in this script , and also tell me that i can change the image effect like fade out or something different please reply as soon as possible

  6. the slider can be working with auto play in the animation?..and how is it done?..thanks!!!!

  7. Your works are really amazing, you’re very talented!
    I hope you can help me because I would like to add differentes titles for each picture & I don’t know how to procede…

    Thanks*
    Keep it up !

  8. Somebody know if Mary Lou answer about if we can show two or more of these galleries in the same page? Thank you!!

  9. Hi, i want some text to come up with each slide. How can i do so to put some text with link

  10. Hi there,

    how can I hide ps_next or ps_prev when the first or the last image is the current one? That’s to say, where do I have to set an if condition to make them invisible (because I think it’s irritating to have them still visible when the first/last picture is selected). Would be great to get a solution for that.

    Anyway, great post as usual, Mary Lou :),

    greetings from Germany,

    Benni

  11. Mary, love this very much. Any simple hints to link the displayed images become clickable to specific urls?

  12. Hello Mary Lou,

    What’s is “$.meta” in line 60?

    o = $.meta ? $.extend({}, opts, this.data()) : opts;

  13. Hello Mary Lou,

    It’s not working for me with my latest version of chrome 11.0.696.71.

    The problem comes from here :

    //the new image width
    //this will be the new width of the ps_image_wrapper
    var newImageWidth = $newImage.width(); ==> return 0;

    we can replace this line by :

    var img = new Image();
    img.src = $image;
    var newImageWidth = img.width;

    it’s not really proper but it works.

    By the way, great post !

    Alban

  14. Español= Hola mary lou,,, me podrias decir porfavor, como agregar más imagenes???,,, gracias.

    English= Hello mary lou,,, can you tell me please, add more images as ???,,, thanks.

  15. Hello Mary Lou,
    Great job, congratulations

    Show me how I can change the path of the images?

    I need the images are inside a subfolder, example: images/nuevacarpeta/1.jpg.

    Thanks

  16. I thought this was going to save my neck…until my client asked for auto-advance and the ability to display sliding captions for each image.

    The way you’ve set up the js and markup, while wonderful in itself, is limited only to your sweet thumbnail slideshow.

    It’s very difficult to incorporate plug ins such as Mosaic, and while I tried “cheating” by slapping a transparent image on top of the gallery, I can only show one caption no matter which thumbnail/image is selected.

    I went into your script and tried modifying it, but could you rewrite your functions so that each image is in its own div with its own hover caption?

    Then my neck will truly be saved!

  17. I really love this slideshow. I’m trying to integrate it with Flickr but the problem it does not load the images properly. Anybody who successfully integrate this cool slideshow with Flickr? Kindly email to jether [at] jether.net . Your help is highly appreciated. Thanks in advance.

  18. Alright guys…My team mate got solution on autoplay problem. She successfully debug it yesterday then I integrated it with Flickr. This is really a very nice slideshow. Congrats and keep up the good work Mary Lou! 🙂

  19. Lovely slide but definitely not working in Safari 5.0.6 and Chrome 13.0.782. It’s working fine in local browsers but not from a server. Can you do something about it please ? Thank you anyway for sharing your work.

  20. BEAUTIFUL, FANTASTIC slideshow, best one I’ve been able to find.

    One question though, is there any way to get the main image to display a caption on a mouseover?

    Many thanks,
    Alan

  21. Can tell me how i can add link to picture in slide? i need action to call sub item from this slide.

  22. Hi Mary Lou,
    Congratulations for your job !
    I need to use it with multiple elements.
    It is possible ?
    Thank for your response

  23. For autoplay function, add this to the end of script:

    setInterval( function(){
    $(‘.ps_next’).click();
    },5000 );

    5000 means, 5 seconds.

  24. Hi, i’m wondering if there’s a way to use this slideshow, create 3 or 4 of them from this, and have them called into the same page and work. I’ve been trying to get 4 or them to load on the same page, but the javascript i believe does not allow for running this slideshow multiple times on a page. any suggestions? thanks :]

  25. The Autoplay works fine but with this

    setInterval( function(){
    $(‘.ps_next’).click();
    },5000 );

    The commas are not working for me…

  26. Sorry publish the same as the other but please take a look in the commas over the ‘.ps_next’. When you copy and paste put other kind of commas instead simple ones…

  27. Is there a way to make it loopable. When you select the las picture and click on next show the first one other time???

  28. Please Help me Mary Lou… Is there a way that when goes to the last pic and click next return to the first one?? Making a loop… Regards

  29. Thanks Mustafa and Juan Pablo. Works fine, but as JP said, pay attention to the single quotes. You may have to replace them.

  30. That script looks very very nice! I downloaded it but there is no js file 🙂 where can I get the js file?