Minimalistic Slideshow Gallery with jQuery

In today’s tutorial we will create a simple and beautiful slideshow gallery that can be easily integrated in your web site. The idea is to have a container with our slideshow and the options to view a grid of thumbnails, pause the slideshow, and navigate through the pictures. The thumbnail grid will […]

View demoDownload source

In today’s tutorial we will create a simple and beautiful slideshow gallery that can be easily integrated in your web site. The idea is to have a container with our slideshow and the options to view a grid of thumbnails, pause the slideshow, and navigate through the pictures. The thumbnail grid will slide out from the top and allow the user to navigate through a set of thumbnails.

So, let’s start!

The Markup

The main HTML structure will consist of the main slideshow wrapper that contains a container for the full view image (msg_wrapper) and one for the thumbnails (msg_thumbs):

<div id="msg_slideshow" class="msg_slideshow">
	<div id="msg_wrapper" class="msg_wrapper"></div>
	<div id="msg_controls" class="msg_controls">
		<a href="#" id="msg_grid" class="msg_grid"></a>
		<a href="#" id="msg_prev" class="msg_prev"></a>
		<a href="#" id="msg_pause_play" class="msg_pause"></a>
		<a href="#" id="msg_next" class="msg_next"></a>
	</div>
	<div id="msg_thumbs" class="msg_thumbs">
		<div class="msg_thumb_wrapper">
			<a href="#">
				<img src="images/thumbs/1.jpg" alt="images/1.jpg"/>
			</a>
			<a href="#">
				<img src="images/thumbs/2.jpg" alt="images/2.jpg"/>
			</a>
			<a href="#">
				<img src="images/thumbs/3.jpg" alt="images/3.jpg"/>
			</a>
			<a href="#">
				<img src="images/thumbs/4.jpg" alt="images/4.jpg"/>
			</a>
			<a href="#">
				<img src="images/thumbs/5.jpg" alt="images/5.jpg"/>
			</a>
			<a href="#">
				<img src="images/thumbs/6.jpg" alt="images/6.jpg"/>
			</a>
		</div>
		<div class="msg_thumb_wrapper" style="display:none;">
			<a href="#">
				<img src="images/thumbs/1.jpg" alt="images/7.jpg"/>
			</a>
			<a href="#">
				<img src="images/thumbs/2.jpg" alt="images/8.jpg"/>
			</a>
			<a href="#">
				<img src="images/thumbs/3.jpg" alt="images/9.jpg"/>
			</a>
			<a href="#">
				<img src="images/thumbs/4.jpg" alt="images/10.jpg"/>
			</a>
			<a href="#">
				<img src="images/thumbs/5.jpg" alt="images/11.jpg"/>
			</a>
			<a href="#">
				<img src="images/thumbs/6.jpg" alt="images/12.jpg"/>
			</a>
		</div>
		<a href="#" id="msg_thumb_next" class="msg_thumb_next"></a>
		<a href="#" id="msg_thumb_prev" class="msg_thumb_prev"></a>
		<a href="#" id="msg_thumb_close" class="msg_thumb_close"></a>
		<span class="msg_loading"></span>
	</div>
</div>

The alt attribute of the thumbnails will contain the path the full view image.
The class names are getting the prefix “msg” so that the style does not interfere with anything else in your web site.
So, let’s take a look at the style.

The CSS

First, we will define the style for the main wrapper:

.msg_slideshow{
	width:400px;
	height:400px;
	padding:10px;
	position:relative;
	overflow:hidden;
	background:#101010 url(../icons/loading.gif) no-repeat center center;
	-moz-border-radius:10px;
	-webkit-border-radius:10px;
	border-radius:10px;
}

By setting the loading GIF to be the background image we are performing a small trick: while we are waiting for the next picture to show, our wrapper will be empty and show the loader. When the next image gets loaded it will simply hide the loader.

We will remove borders and outlines from the link elements:

.msg_slideshow a{
	outline:none;
}
.msg_slideshow a img{
	border:none;
}

In order to center the full image in the container, both, vertically and horizontally, we need to add another wrapper around. That wrapper will be displayed as a table cell. Adding the vertical-align:middle property will center the image.

.msg_wrapper{
	width:400px;
	height:400px;
	position:relative;
	margin:0;
	padding:0;
	display:table-cell;
	text-align:center;
	vertical-align:middle;
	overflow:hidden;
}
.msg_wrapper img{
	display: inline-block!important;
	vertical-align:middle;
	-moz-box-shadow:0px 0px 10px #000;
	-webkit-box-shadow:0px 0px 10px #000;
	box-shadow:0px 0px 10px #000;
}

The display property in the image style will make sure that our image does not become display:block because of the fadeIn we will use in our jQuery function. Since we want to keep our image centered horizontally, we need to keep is as an inline (-block) element.

The control element will have the following style:

.msg_controls{
	position:absolute;
	bottom:15px;
	right:-110px;
	width:104px;
	height:26px;
	z-index: 20;
	-moz-border-radius:5px;
	-webkit-border-radius:5px;
	border-radius:5px;
	background-color:#000;
	opacity:0.8;
}

The common style for all the control elements will be as follows:

.msg_controls a{
	float:left;
	background-color:#000;
	width:20px;
	height:20px;
	margin:3px 3px;
	opacity:0.5;
	background-repeat:no-repeat;
	background-position: center center;
}
.msg_controls a:hover{
	opacity:1.0;
}

The style for each icon:

.msg_controls a.msg_grid{
	background-image:url(../icons/grid.png);
}
.msg_controls a.msg_prev{
	background-image:url(../icons/prev.png);
}
.msg_controls a.msg_next{
	background-image:url(../icons/next.png);
}
.msg_controls a.msg_pause{
	background-image:url(../icons/pause.png);
}
.msg_controls a.msg_play{
	background-image:url(../icons/play.png);
}

The thumbs container will slide in from the top, so we will position it absolutely and hide it initially by setting the top to -230px:

.msg_thumbs{
	background:#000;
	position:absolute;
	width:250px;
	height:166px;
	top:-230px;
	left:50%;
	padding:30px;
	margin:0 0 0 -155px;
	-moz-border-radius:0px 0px 10px 10px;
	-webkit-border-bottom-left-radius:10px;
	-webkit-border-bottom-right-radius:10px;
	border-bottom-left-radius:10px;
	border-bottom-right-radius:10px;
	-moz-box-shadow:1px 1px 5px #000;
	-webkit-box-shadow:1px 1px 5px #000;
	box-shadow:1px 1px 5px #000;
	opacity:0.9;
	overflow:hidden;
}

With a left of 50% and a left margin of minus half of its width, we can center the absolute element horizontally.

The wrapper containing the thumbnails will have the following style:

.msg_thumb_wrapper{
	position:absolute;
	width:250px;
	height:166px;
	top:30px;
	left:30px;
	z-index:30;
}

The style of the thumbnails will be as follows:

.msg_thumb_wrapper a{
	display:block;
	width:75px;
	height:75px;
	float:left;
	margin:4px;
	opacity:0.8;
}

When we hover the thumbnail we will set the opacity to 1.0, but that we will animate in our jQuery function for a nice effect.

The style for the navigation controls:

.msg_thumbs a.msg_thumb_next,
.msg_thumbs a.msg_thumb_prev{
	width:18px;
	height:20px;
	background-repeat:no-repeat;
	background-position: center center;
	position:absolute;
	top:50%;
	margin-top:-10px;
	opacity:0.5;
}
.msg_thumbs a.msg_thumb_next:hover,
.msg_thumbs a.msg_thumb_prev:hover{
	opacity:1.0;
}
.msg_thumbs a.msg_thumb_next{
	background-image:url(../icons/next_thumb.png);
	right:5px;
}
.msg_thumbs a.msg_thumb_prev{
	background-image:url(../icons/prev_thumb.png);
	left:5px;
}

The little element for sliding back the grid view:

.msg_thumbs a.msg_thumb_close{
	position:absolute;
	bottom:0px;
	width:50px;
	left:50%;
	margin:0 0 0 -25px;
	background:#202020 url(../icons/up.png) no-repeat center center;
	height:16px;
	opacity:0.7;
	-moz-border-radius:5px 5px 0 0;
	-webkit-border-top-left-radius:5px;
	-webkit-border-top-right-radius:5px;
	border-top-left-radius:5px;
	border-top-right-radius:5px;
}

We are adding some border radius to the top right and top left corners. When we hover, we will make it opaque:

.msg_thumbs a.msg_thumb_close:hover{
	opacity:1.0;
}

And finally, the loading element for the grid view which we center horizontally and vertically with our 50% method:

.msg_loading{
	position:absolute;
	background:transparent url(../icons/loading.gif) no-repeat center center;
	top:50%;
	left:50%;
	width:50px;
	height:50px;
	margin:-25px 0 0 -25px;
	z-index:25;
	display:none;
}

And that’s all the style. Now, let’s add some magic!

The JavaScript

First, we will define some variables:

  • interval: time between the display of images
  • playtime: the timeout for the setInterval function
  • current: number to control the current image
  • current_thumb: the index of the current thumb wrapper
  • nmb_thumb_wrappers: total number of thumb wrappers
  • nmb_images_wrapper: the number of images inside of each wrapper
var interval			= 4000;
var playtime;
var current 			= 0;
var current_thumb 		= 0;
var nmb_thumb_wrappers	= $('#msg_thumbs .msg_thumb_wrapper').length;
var nmb_images_wrapper  = 6;

We start the slideshow:

play();

When we hover over the main container, we will make the controls slide in from the right:

slideshowMouseEvent();
function slideshowMouseEvent(){
	$('#msg_slideshow').unbind('mouseenter')
					   .bind('mouseenter',showControls)
					   .andSelf()
					   .unbind('mouseleave')
					   .bind('mouseleave',hideControls);
	}

When we click on the grid icon in the controls we will show the thumbnails view, pause the slideshow, and hides the control:

$('#msg_grid').bind('click',function(e){
	hideControls();
	$('#msg_slideshow').unbind('mouseenter').unbind('mouseleave');
	pause();
	$('#msg_thumbs').stop().animate({'top':'0px'},500);
	e.preventDefault();
});

Clicking on the icon to hide the thumbnails view will again show the controls:

$('#msg_thumb_close').bind('click',function(e){
	showControls();
	slideshowMouseEvent();
	$('#msg_thumbs').stop().animate({'top':'-230px'},500);
	e.preventDefault();
});

Next, we define what happens when we click pause or play. Besides changing the class, we will call the pause() or play() function:

$('#msg_pause_play').bind('click',function(e){
	var $this = $(this);
	if($this.hasClass('msg_play'))
		play();
	else
		pause();
	e.preventDefault();
});

Clicking on “next” or “previous” control will pause our slideshow and show the respective image:

$('#msg_next').bind('click',function(e){
	pause();
	next();
	e.preventDefault();
});
$('#msg_prev').bind('click',function(e){
	pause();
	prev();
	e.preventDefault();
});

Next, we define the functions for showing and hiding the controls. By setting the right value, we will make it slide. Remember, that we set an initial negative value in the CSS.

function showControls(){
	$('#msg_controls').stop().animate({'right':'15px'},500);
}
function hideControls(){
	$('#msg_controls').stop().animate({'right':'-110px'},500);
}

The play() function which makes the slideshow go:

function play(){
	next();
	$('#msg_pause_play').addClass('msg_pause').removeClass('msg_play');
	playtime = setInterval(next,interval)
}

The function for pausing the slideshow. We change again the class and clear the timeout:

function pause(){
	$('#msg_pause_play').addClass('msg_play').removeClass('msg_pause');
	clearTimeout(playtime);
}

The next two function will show the next or previous image:

function next(){
	++current;
	showImage('r');
}
function prev(){
	--current;
	showImage('l');
}

The next function is for showing the image. We also call alternateThumbs(), which will always change the grid to display the view where the current image is located.

function showImage(dir){
	/**
	* the thumbs wrapper being shown, is always 
	* the one containing the current image
	*/
	alternateThumbs();
	
	/**
	* the thumb that will be displayed in full mode
	*/
	var $thumb = $('#msg_thumbs .msg_thumb_wrapper:nth-child('+current_thumb+')')
				.find('a:nth-child('+ parseInt(current - nmb_images_wrapper*(current_thumb -1)) +')')
				.find('img');
	if($thumb.length){
		var source = $thumb.attr('alt');
		var $currentImage = $('#msg_wrapper').find('img');
		if($currentImage.length){
			$currentImage.fadeOut(function(){
				$(this).remove();
				$('<img />').load(function(){
					var $image = $(this);
					resize($image);
					$image.hide();
					$('#msg_wrapper').empty().append($image.fadeIn());
				}).attr('src',source);
			});
		}
		else{
			$('<img />').load(function(){
					var $image = $(this);
					resize($image);
					$image.hide();
					$('#msg_wrapper').empty().append($image.fadeIn());
			}).attr('src',source);
		}
				
	}
	else{ //this is actually not necessary since we have a circular slideshow
		if(dir == 'r')
			--current;
		else if(dir == 'l')
			++current;	
		alternateThumbs();
		return;
	}
}

The thumbs wrapper being shown is always the one containing the current image. So, we define a function that controls this:

function alternateThumbs(){
	$('#msg_thumbs').find('.msg_thumb_wrapper:nth-child('+current_thumb+')')
					.hide();
	current_thumb = Math.ceil(current/nmb_images_wrapper);
	/**
	* if we reach the end, start from the beggining
	*/
	if(current_thumb > nmb_thumb_wrappers){
		current_thumb 	= 1;
		current 		= 1;
	}
	/**
	* if we are at the beggining, go to the end
	*/
	else if(current_thumb == 0){
		current_thumb 	= nmb_thumb_wrappers;
		current 		= current_thumb*nmb_images_wrapper;
	}

	$('#msg_thumbs').find('.msg_thumb_wrapper:nth-child('+current_thumb+')')
					.show();
}

Next, we define what happens when we navigate through the thumbs:

$('#msg_thumb_next').bind('click',function(e){
	next_thumb();
	e.preventDefault();
});
$('#msg_thumb_prev').bind('click',function(e){
	prev_thumb();
	e.preventDefault();
});
function next_thumb(){
	var $next_wrapper = $('#msg_thumbs').find('.msg_thumb_wrapper:nth-child('+parseInt(current_thumb+1)+')');
	if($next_wrapper.length){
		$('#msg_thumbs').find('.msg_thumb_wrapper:nth-child('+current_thumb+')')
						.fadeOut(function(){
							++current_thumb;
							$next_wrapper.fadeIn();
						});
	}
}
function prev_thumb(){
	var $prev_wrapper = $('#msg_thumbs').find('.msg_thumb_wrapper:nth-child('+parseInt(current_thumb-1)+')');
	if($prev_wrapper.length){
		$('#msg_thumbs').find('.msg_thumb_wrapper:nth-child('+current_thumb+')')
						.fadeOut(function(){
							--current_thumb;
							$prev_wrapper.fadeIn();
						});
	}
}

Clicking on a thumbnail will load the respective image:

$('#msg_thumbs .msg_thumb_wrapper > a').bind('click',function(e){
	var $this 		= $(this);
	$('#msg_thumb_close').trigger('click');
	var idx			= $this.index();
	var p_idx		= $this.parent().index();
	current			= parseInt(p_idx*nmb_images_wrapper + idx + 1);
	showImage();
	e.preventDefault();
}).bind('mouseenter',function(){
	var $this 		= $(this);
	$this.stop().animate({'opacity':1});
}).bind('mouseleave',function(){
	var $this 		= $(this);
	$this.stop().animate({'opacity':0.5});
});

And finally, our resize function that fits the image inside of our container, which we defined to be 400×400 pixel:

function resize($image){
	var theImage 	= new Image();
	theImage.src 	= $image.attr("src");
	var imgwidth 	= theImage.width;
	var imgheight 	= theImage.height;

	var containerwidth  = 400;
	var containerheight = 400;

	if(imgwidth	> containerwidth){
		var newwidth = containerwidth;
		var ratio = imgwidth / containerwidth;
		var newheight = imgheight / ratio;
		if(newheight > containerheight){
			var newnewheight = containerheight;
			var newratio = newheight/containerheight;
			var newnewwidth =newwidth/newratio;
			theImage.width = newnewwidth;
			theImage.height= newnewheight;
		}
		else{
			theImage.width = newwidth;
			theImage.height= newheight;
		}
	}
	else if(imgheight > containerheight){
		var newheight = containerheight;
		var ratio = imgheight / containerheight;
		var newwidth = imgwidth / ratio;
		if(newwidth > containerwidth){
			var newnewwidth = containerwidth;
			var newratio = newwidth/containerwidth;
			var newnewheight =newheight/newratio;
			theImage.height = newnewheight;
			theImage.width= newnewwidth;
		}
		else{
			theImage.width = newwidth;
			theImage.height= newheight;
		}
	}
	$image.css({
		'width'	:theImage.width,
		'height':theImage.height
	});
}

And that’s it! We hope you enjoyed this tutorial and find our little slideshow useful!

View demoDownload source

Previous:
Next:

Tagged with:

ML is a freelance web designer and developer with a passion for interaction design. She studied Cognitive Science and Computational Logic and has a weakness for the smell of freshly ground peppercorns.

View all contributions by

Website: http://www.codrops.com

Related Articles

Receive our bi-weekly Collective or official newsletter right in your inbox.

Which newsletter would you like to receive?

CSS Reference

Learn about all important CSS properties from the basics with our extensive and easy-to-read CSS Reference.

It doesn't matter if you are a beginner or intermediate, start learning CSS now.

Feedback 66

Comments are closed.
  1. 1

    @Karina, you will have to add them to the html file. For every set of 6 images you add a wrapper called “msg_thumb_wrapper”. Just take a look at the file and you will see the structure. Don’t forget to add the path to the bigger image to the “alt” attribute. Hope it helps, cheers, ML

  2. 2

    this is a great gallery but I’ve got a problem. when i did the same thing it worked on my computer but when i transfered it online the icons for play, next, … did’nt play.

    Why?

  3. 4

    Hi Mary Lou,
    i love your “minimalism” but i was wondering if it coul be possible to have a button to enlarge the images. I’ll explain myself : in your example the images in the slideshow are 400px width but could it be larger ones, 800px for example, resized by the javascript (i guess it’s already done). The point is, how to implement it in the js.
    I think it counter your aim to be minimalist and if it’s possible you could only give me the trick in the js. Tks. L

  4. 7

    Hey ML,
    Like a few have mentioned before I’d like to emphasize on a few things.
    #1 Love your work. Quality and quantity.

    #2 I agree with @Jireck and much appreciate the ul li implementation.

    #3 @Ludo I also need to enlarge the images one more time. Perhaps with a simple lightbox effect.

    Using Fancybox requires an anchor tag to be wrapped around the image. Is this possible?

    Many thanks. Cheers!

  5. 8

    Hi, Great work.

    How is it possible to open external pages through the image links.

    Do I need an onClick event in the HTML?

    Thanks

  6. 9

    Thanks for this wonderful slideshow. I actually use it in my website. How about putting caption? I hope you can develop that one.. Great thanks! =)

  7. 10

    Hello Mary Lou,

    (couldn´t resist that…;-) )

    great slideshow and very good tutorial. I´ve got one question: at the moment the slideshow stops on the last image. Is it possible to put it on endless repeat?

    Cheers
    ingo

  8. 11

    Fantastic work! Codrops is generally my first stop when looking for cool jQuery driven things.

    I second Rolly’s request for captions in the gallery. Would love to have the it appear as an image overlay.

  9. 12

    Thanks buddy it’s awesome. But i have query. How to play this slide show in loop?

  10. 15

    How do you make the transitions crossfade instead of fading to black and then fade to next photo?

  11. 16

    first off, lovely script =)

    one issue i’m having is with using rather than lists to list the thumbnails.. for some reason this seems to make the gallery stick when it reaches the end of one page of thumbnails, both when clicking the next arrow or simply playing through causing a user issue when they don’t realise there are more image.. any ideas?

  12. 17

    sorry in previous post it took out the html, i meant i’m using ordered lists rather than unordered

  13. 18

    nevermind, i simply hadn’t updated the number of thumbnails per page.. =)

Comments are closed.