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

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!

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 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!

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

Fresh news, inspo, code demos, and UI animations—zero fluff, all quality. Make your Mondays and Thursdays creative!

Feedback 66

Comments are closed.
  1. This is very well done but I still prefer to have thumbnails generated automatically and (future improvement) maybe have an option to load images from Flickr or a folder.
    Well DONE!!

  2. Very nice, any chance of added the ability to add a comment to each pic that slides up as the pic is shown?

  3. Great !!
    Wonderfull slideshow and use of jquery but :

    – Why you dont use UL with LI to display image (I dont know why but i dont like many following link : A HREF=’#’) ?

    – I Think Your demo is ….too dark

    Jireck

  4. Hi Jireck! You are right, I am link obsessed, someone should slap me! 🙂 I will keep that in mind for the next tuts. In the meantime, here is version with list elements and in light color:
    DEMO
    ZIP
    What do you say?
    Cheers, ML

  5. wow Mary Lou make different versi but its cool
    its cool and thats cool = all cool
    WOW!!!

  6. This gallery is fantastic – have shown it to a few here at work – they just loved it too 🙂 Thanks for this tutorial.

  7. Hi Mary Lou

    The demo is really Nice ….

    I think it’s more clean to do with UL LI…

    I will check code (i DL your zip)

    thanks

    Jireck
    PS : Reactivity increase !!! 😉

  8. Loved the gallery just what I am looking for. I need a little help though. If i try to put the gallery twice in one page, the one load perfectly the other, just has the loading sign, any idea why? Since I would like to put 3 on one page.

  9. Thank you very much
    I am using it and it looks amazing.
    Code explanation made it easy for me to customize it for my site.

  10. Hello! It’s really fantastic gallery! But I have the question – script works with only 12 images and I can’t find how expand this size. There are about 100 images in my folder and I’d like to show they all.

  11. @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

  12. 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?

  13. 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

  14. 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!

  15. 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

  16. 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! =)

  17. 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

  18. 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.

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

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

  21. 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?

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

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

  24. Wow, it’s so beautiful and would be great to use in any WordPress premium theme.

    Could you change it to a WP plugin for more easy to use?

  25. Mary Lou,

    You are a goddess!!! I had been looking for a simple slideshow that did not use Flash and could be edited fairly quickly for my samples portfolio site. Out of literally hundreds of templates I searched through, yours was ideal. AND, even though I’m a total novice, I was able to muddle through the code and edit it in myself in order size it up slightly (see below). I’m sure the code is a bit messy but it seems to work.

    Thanks again!

    http://www.mscs-ny.com/work.html

  26. In IE when resize image at 400×400, not work
    Shadow and rounded corners not work
    Any solution?
    Thanks

  27. Hi Mary Lou.
    Very nice piece of work. I love the simplicity of the code.
    One question… you are using a setInterval() which is a continuous process until stopped. I would like to have one slide show longer; ie, change the ‘interval’ variable on the basis of the ‘current’ variable. Is there a way to interject a new ‘interval’ value during the setInterval()?

  28. Hi ! nice nice nice !
    but I have some problem with this!
    How is it possible to open external pages through the image links.
    plz help me ! plzzzzz!
    it’s really important for me!
    is it possibal?
    tanx

  29. Very nice! Works with 1.6.2.

    I suggest making the thumbnail area “next” and “back” arrows wrap around, or not appear on the last and first thumbs panels, respectively, rather than being dead links.

  30. Hi Mary Lou,

    Great job!!! Very nice!
    I have one problem…I want to put link on every big image. How can I do that?

    Thanks a lot!