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. Thanks for a great slideshow gallery. It’s just what I was after for my soon to be released on-line store. Just for your info, it’s easy to integrate it into Open Cart!

  2. Anybody get this working in a Ruby on Rails application? I have most of it working except it does not seem to recognize when I hover over the main container, to make the controls slide in from the right. Instead, I actually have to click on the container to make them appear. Very cool slide show. Thanks!

  3. What I wouldn’t give to have captions available for this script. Would be absolutely perfect, already awesome.

  4. i have tried all slide shows they are best in design but they are not working when i upload them on my windows based server 🙁 i have tried all the slideshows plz help me :(((

  5. Hey can u help me? what if I want to use more than one slider on one page ?

    is there a simple way to differentiate the ID ?

  6. Hi!
    I think It would be nice if you explain easily how should we do if we want to add more than 6 photos in the gallery, so the people looking for an easy gallery will find yours amazing and extremely easy to adapt in their sites.

    You say that we need to add a wrapper for every 6 images… doing this is enough to make the gallery works fine? or we need to do something else?

    Thanks for share your work, it’s so awesome and easy to use in any site!

  7. Hi!

    Thanks for the lovely template.

    I would like to ask how I can remove the black background and just have the controls at the bottom of the photos…

    Thanks 🙂