Thumbnails Navigation Gallery with jQuery

In this tutorial we are going to create an extraordinary gallery with scrollable thumbnails that slide out from a navigation. We are going to use jQuery and some CSS3 properties […]

In this tutorial we are going to create an extraordinary gallery with scrollable thumbnails that slide out from a navigation. We are going to use jQuery and some CSS3 properties for the style. The main idea is to have a menu of albums where each item will reveal a horizontal bar with thumbnails when clicked. The thumbnails container will scroll automatically when the user moves the mouse to the left or right.

When a thumbnail is clicked it will be loaded as a full image preview in the background of the page. We will also have a text container for one of the menu items.

The beautiful photos are from Mark Sebastian’s photostream on Flickr. You can find all the images used in the demo in the set The “IT” Factor. Please review the Creative Commons license that is included in the demo.

So, let’s get started!

The Markup

Our HTML is mainly going to consist of a wrapper and the menu list. We will have some other elements, like the full image, the loading div and the halftone overlay. First, let’s create the wrapper:

<div id="st_main" class="st_main">

</div>

Inside of our wrapper we will add the following:

<img src="images/album/1.jpg" alt="" class="st_preview" style="display:none;"/>

<div class="st_overlay"></div>

<h1>Mark Sebastian</h1>

<div id="st_loading" class="st_loading">
	<span>Loading...</span>
</div>

The first element is our full preview image. The overlay is going to be a fixed div which will stretch over the whole screen repeating a halftone pattern to create a fancy overlay effect on the image. We also add a title and a loading div.

We then add an unordered list where each li element is going to contain a span for its title and the thumbnails wrapper. The last li element is going to contain some wrapper for text, that’s why we will not give it the class “album”. Later, in the jQuery function we will need to distinguish that.

<ul id="st_nav" class="st_navigation">
	<li class="album">
		<span class="st_link">
			Newest Collection
			<span class="st_arrow_down"></span>
		</span>
		<div class="st_wrapper st_thumbs_wrapper">
			<div class="st_thumbs">
				<img src="images/album/thumbs/1.jpg" alt="images/album/1.jpg"/>
				<img src="images/album/thumbs/2.jpg" alt="images/album/2.jpg"/>
				...
			</div>
		</div>
	</li>
	...
	<li>
		<span class="st_link">
			About
			<span class="st_arrow_down"></span>
		</span>
		<div class="st_about st_thumbs_wrapper">
			<div class="st_subcontent">
				<p>
					Some description
				</p>
			</div>
		</div>
	</li>
</ul>

The thumbnail images get the alt value of the path to the full size image. That might not be the proper use of the alt attribute, but it’s just so convenient for our functionality that we will use it this way.

Let’s take a look at the style.

The CSS

First, let’s reset the paddings and margins for all elements and define the general styles:

*{
	margin:0;
	padding:0;
}
body{
	font-family:"Myriad Pro","Trebuchet MS", Helvetica, sans-serif;
	font-size:16px;
	color:#fff;
	background-color:#000;
	overflow:hidden;
}
h1{
	margin:20px;
	font-size:40px;
}

Making the body overflow hidden we avoid any scroll bar appearing, but you can adapt that to your needs. I.e. if it is important that the full sized image is completely viewable by the user, you might want to remove the overflow:hidden property.

Next, we will define the style for the full size image, the overlay and the loading div:

.st_main img.st_preview{
	position:absolute;
	left:0px;
	top:0px;
	width:100%;
}
.st_overlay{
	width:100%;
	height:100%;
	position:fixed;
	top:0px;
	left:0px;
	background:transparent url(../images/pattern.png) repeat-x bottom left;
	opacity:0.3;
}
.st_loading{
	position:fixed;
	top:10px;
	right:0px;
	background:#000 url(../images/icons/loader.gif) no-repeat 10px 50%;
	padding:15px 40px 15px 60px;
	-moz-box-shadow:0px 0px 2px #000;
	-webkit-box-shadow:0px 0px 2px #000;
	box-shadow:0px 0px 2px #000;
	opacity:0.6;
}

By setting the image width to be always 100%, we make sure that it occupies all the horizontal space on the page. For very large screens the image might look pixelated which can, of course, be avoided by using gigantic images. In our demo we use a maximum width of 1600 pixel to make the loading time bearable. The halftone pattern on top of the image helps a little to disguise a pixelated effect.

Please note that whenever opacity is used, the IE filter property needs to be used if you want to achieve semi-transparent effects in IE. The overlay looks like crap if you use the filter property, though. Check out the ZIP file, I added the IE DXImageTransform filter to the respective styles.

The navigation will be positioned absolutely:

ul.st_navigation{
	position:absolute;
	width:100%;
	top:140px;
	left:-300px;
	list-style:none;
}

The initial left value is set to -300 pixel because we want to slide it in only after our full image is loaded. If you use longer titles in the list items, you might need to adapt this value.

Our list elements are going to have the following style:

ul.st_navigation li {
	float:left;
	clear:both;
	margin-bottom:8px;
	position:relative;
	width:100%;
}

The span for the title will be styled as follows:

ul.st_navigation li span.st_link{
	background-color:#000;
	float:left;
	position:relative;
	line-height:50px;
	padding:0px 20px;
	-moz-box-shadow:0px 0px 2px #000;
	-webkit-box-shadow:0px 0px 2px #000;
	box-shadow:0px 0px 2px #000;
}

Next, we define the style for the spans with the up/down arrow for opening and closing the thumbnail container:

ul.st_navigation li span.st_arrow_down,
ul.st_navigation li span.st_arrow_up{
	position:absolute;
	margin-left:15px;
	width:40px;
	height:50px;
	cursor:pointer;
	-moz-box-shadow:0px 0px 2px #000;
	-webkit-box-shadow:0px 0px 2px #000;
	box-shadow:0px 0px 2px #000;
}
ul.st_navigation li span.st_arrow_down{
	background:#000 url(../images/icons/down.png) no-repeat center center;
}
ul.st_navigation li span.st_arrow_up{
	background:#000 url(../images/icons/up.png) no-repeat center center;
}

The wrapper for the thumbnail container will be positioned absolutely and we want to hide any vertical overflow:

.st_wrapper{
	display:none;
	position: absolute;
    width:100%;
    height:126px;
    overflow-y:hidden;
	top:50px;
    left:0px;
}

Although the thumbs are only 120 pixels high, we want to leave some space so that the box shadow of the images inside will not be cut away.
The thumbnails container will simply be styled as follows:

.st_thumbs{
    height:126px;
    margin: 0;
}

The thumbnails will have a neat box shadow and some spacing:

.st_thumbs img{
    float:left;
    margin:3px 3px 0px 0px;
    cursor:pointer;
	-moz-box-shadow:1px 1px 5px #000;
	-webkit-box-shadow:1px 1px 5px #000;
	box-shadow:1px 1px 5px #000;
	opacity:0.7;
}

The st_about class is the wrapper class for the text container:

.st_about{
	display:none;
	position:absolute;
	top:50px;
    left:0px;
	opacity:0.6;
}

And, finally the text container itself:

.st_subcontent{
	background:#000;
	padding:30px;
	-moz-box-shadow:0px 0px 10px #000;
	-webkit-box-shadow:0px 0px 10px #000;
	box-shadow:0px 0px 10px #000;
}

And that’s all the style! Let’s make some magic!

The JavaScript

In our jQuery function we will first define some variables:

//the loading image
var $loader		= $('#st_loading');
//the ul element
var $list		= $('#st_nav');
//the current image being shown
var $currImage 	= $('#st_main').children('img:first');

The first thing that we want to do is to load the current full size image. After it’s loaded, we want the navigation to appear:

$('<img>').load(function(){
	$loader.hide();
	$currImage.fadeIn(3000);
	//slide out the menu
	setTimeout(function(){
		$list.animate({'left':'0px'},500);
	},
	1000);
}).attr('src',$currImage.attr('src'));

The buildThumbs() function calculates the widths of all the thumbnail containers. We need that value for the automatic scrolling function later on:

buildThumbs();

function buildThumbs(){
	$list.children('li.album').each(function(){
		var $elem 			= $(this);
		var $thumbs_wrapper = $elem.find('.st_thumbs_wrapper');
		var $thumbs 		= $thumbs_wrapper.children(':first');
		//each thumb has 180px and we add 3 of margin
		var finalW 			= $thumbs.find('img').length * 183;
		$thumbs.css('width',finalW + 'px');
		//make this element scrollable
		makeScrollable($thumbs_wrapper,$thumbs);
	});
}

Next, we define the behavior of clicking on the arrows. If it’s down, we will expand the thumbnail container and hide any other. If it’s up, we will make the current container hide again.

$list.find('.st_arrow_down').live('click',function(){
	var $this = $(this);
	hideThumbs();
	$this.addClass('st_arrow_up').removeClass('st_arrow_down');
	var $elem = $this.closest('li');
	$elem.addClass('current').animate({'height':'170px'},200);
	var $thumbs_wrapper = $this.parent().next();
	$thumbs_wrapper.show(200);
});
$list.find('.st_arrow_up').live('click',function(){
	var $this = $(this);
	$this.addClass('st_arrow_down').removeClass('st_arrow_up');
	hideThumbs();
});

Before we define the hideThumbs() function, we specify what happens when clicking on a thumb. The full size image will show and while it’s loading we will make the loading div appear. Then we animate the opacity and fade the image in:

$list.find('.st_thumbs img').bind('click',function(){
	var $this = $(this);
	$loader.show();
	$('<img class="st_preview"/>').load(function(){
		var $this = $(this);
		var $currImage = $('#st_main').children('img:first');
		$this.insertBefore($currImage);
		$loader.hide();
		$currImage.fadeOut(2000,function(){
			$(this).remove();
		});
	}).attr('src',$this.attr('alt'));
}).bind('mouseenter',function(){
	$(this).stop().animate({'opacity':'1'});
}).bind('mouseleave',function(){
	$(this).stop().animate({'opacity':'0.7'});
});

The function to hide the thumbnails looks as follows:

function hideThumbs(){
	$list.find('li.current')
		 .animate({'height':'50px'},400,function(){
			$(this).removeClass('current');
		 })
		 .find('.st_thumbs_wrapper')
		 .hide(200)
		 .andSelf()
		 .find('.st_link span')
		 .addClass('st_arrow_down')
		 .removeClass('st_arrow_up');
}

We animate the height of the li and make the thumbnails container disappear like that. We also need to set the class for the arrow span correctly.

And finally, we will define the makeScrollable() function that automatically scrolls the thumbnails div horizontally on mouse move:

function makeScrollable($outer, $inner){
	var extra 			= 800;
	//Get menu width
	var divWidth = $outer.width();
	//Remove scrollbars
	$outer.css({
		overflow: 'hidden'
	});
	//Find last image in container
	var lastElem = $inner.find('img:last');
	$outer.scrollLeft(0);
	//When user move mouse over menu
	$outer.unbind('mousemove').bind('mousemove',function(e){
		var containerWidth = lastElem[0].offsetLeft + lastElem.outerWidth() + 2*extra;
		var left = (e.pageX - $outer.offset().left) * (containerWidth-divWidth) / divWidth - extra;
		$outer.scrollLeft(left);
	});
}

For cufonizing the titles and adding some decent shadow, we will include the following into the head of our html document:

<script src="js/cufon-yui.js" type="text/javascript"></script>
<script src="js/Quicksand_Book_400.font.js" type="text/javascript"></script>
<script type="text/javascript">
	Cufon.replace('span,p,h1',{
		textShadow: '0px 0px 1px #ffffff'
	});
</script>

And that’s it! We hope you enjoyed the tutorial and find it useful!

Message from TestkingWe offer exceptional 220-702 training program to help you pass sun certification in easy and fast way. Complete your CISSP certification within days using certified resources.

Tagged with:

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 up to date with the latest web design and development news and relevant updates from Codrops.

Feedback 249

Comments are closed.
  1. What causes the h1 heading to fade out when the menus come in if the font is changed and the java script font is removed – also how do you auto close the menu after clicking on a photo?

  2. @MATTHEW
    For the css h1 problem, you have to send the image layer lower, by setting the z-index:-2; inside the bracket of css class (css file)
    .st_main img.st_preview{
    z-index:-2

    And for auto-hide menu, you have to copy the hiding function (only this text)
    hideThumbs();
    right under
    }).attr(‘src’,$this.attr(‘alt’));
    in the $list.find(‘.st_thumbs img’).bind(‘click’,function(){

    hoping you understand my instruction

  3. First of all AMAZING work! On to my question:

    Q: How can I expand the top menu field on default? So when you navigate to the website, the first field is expanded. I want to fill it with some Welcome text.

  4. Is there any way to have two menus – one on the left hand side and one on the right hand side

    and can the menu block scroll if there are more options than page height?

    and thanks @DENISD

  5. 2nd question:

    Why does the about div overlap the menu buttons below it when you put lets say a video frame in it? It doesn’t align as it should…

    Even with the modification posted here in the comments, to enable scrolling…

    Many thanks in advance who ever can answer both of my questions!

  6. @LJEH
    Because the whole structure of the this navigation is base on the this() function on the click event.
    iv simulate a click event by programmatically pointing x, y to the first arrow block. (only the one on top works because it didn’t move when the other menu’ click…if someone click elsewhere before the function autoclick). It must be trigger with a delay to wait for the menu finishing pan right. So put this code
    setTimeout(function(){
    $(document.elementFromPoint(200,165)).click();
    },
    6000);
    in the first section of javascript, right after }).attr(‘src’,$currImage.attr(‘src’));
    200, 165 refer to x,y coord…so can be different depending the lenght of the menu title
    and the 6000 refer to time to wait until trigger…its up to you.

  7. I don’t think that it is possible but could the image drop down split the background image – like when you open a folder on iOS – i don’t think it is using anything like the current script but just curious πŸ™‚

  8. Hi – is it possible for the background to be 100% high or 100% wide – so that it fills the browser

    also can the initial image displayed be randomised? i can do it via a horrible method using http://javascriptsource.com/miscellaneous/random-image.html onto of the actual first image πŸ™ is there any way to embed this function into the initial part of the script – i have tired but no cigar!

    http://p3.phcd.co.uk/

    is where i am now – as of now – still trying i can’t get the navigation buttons at the bottom to work

    if anyone could help me fix the buttons or make the image fill the frame

    i would be so happy πŸ™‚

  9. @DENISD:

    Is your code the answer to my first or second question?

    I guess it’s an answer to my question about expanding the first menu item by default?

    I’ve tried your code and modified the x and y values, but it doesn’t work. I’ve also changed the 6000ms thingy…

    Any help would be greatly appreciated!

    Thanks DENISD.

  10. @LJEH the answer setTimeout(function(){
    $(document.elementFromPoint(200,165)).click();
    },
    6000);
    expands the menu – so your first question – it simulated the end user clicking on the first drop down position – if you have typed a different length of text in the st_link area you may have to modify the x area – if your on a mac – the resizer 0.4 extension thing is a good ruler tool πŸ™‚ – someone else posted about videos btw
    have a look at my code – on client images as an alternative for something like a video πŸ™‚ http://www.p3.phcd.co.uk

  11. Thanks mate, my text is “Welkom” in Dutch. Could you please check the X and Y coordinates for me. I don’t have a Mac :).

  12. @ LJEH – – – If you upload it to somewhere i can – iv moved mine all around – so it would be better if i measure your πŸ™‚

  13. I’m going to upload it tonight and post a link mate.

    About the video. I just want the video to be in a text area. It does not have to be in the gallery style with a thumbnail and stuff.

    The problem is that the video has to be incorporated in the last but one menu item. When you open the last but one menu item the video gets overlapped over the last menu item.

    I will post a link tonight, that clarifies things a lot πŸ˜› .

    Cheers

  14. @LJEH its easy – if you look at my code – you will see how i have made a second on arrow left/right sequence – make it the same as the on arrow down but change the height form 180 to the new height – the video height! – – – simples πŸ™‚
    it will collapse to the same height – just look at what i did for my code!!

  15. On my build i have tried to add navigational buttons up down to switch the collections and left right ,for the current collection, to swap the image – they would preferably loop so after the last going to the first – is this possible iv tried blending this tutorial with http://tympanus.net/codrops/2010/09/08/full-page-image-gallery/ but no luck yet!

    Is it possible for the background to be 100% high or 100% wide – so that it fills the browser?

    also can the initial image displayed be randomised? i can do it via a horrible method using http://javascriptsource.com/miscellaneous/random-image.html onto of the actual first image πŸ™ is there any way to embed this function into the initial part of the script – i have tired but no cigar!

    http://p3.phcd.co.uk/

    is where i am now – as of now – still trying i can’t get the navigation buttons at the bottom to work

    if anyone could help me fix the buttons or make the image fill the frame

    i would be so happy πŸ™‚

  16. Several people advised to put hideThumbs() on line 211 to auto-close the thumbs bar. But when I implement this, you have to click twice on the arrow, because it thinks it is still opened and not closed.

    Any ideas? Please I need your help!

  17. @Mathew
    How did you solved the height problem? That is, when a photo is vertical its height attribute is set to 100% and when a photo is horizontal its widht attribute is set to 100%?

    I created and tested the code below and it does what I said above. The problem is I don’t know how or where to run or call it on the index.html page.

    function resizeToMax(id) {
    var img = document.getElementById(id);
    myImage = new Image();
    myImage.src = img.src;
    if(myImage.width > myImage.height) { /* landscape */
    img.style.width = “100%”;
    } else { /* portrait */
    img.style.height = “100%”;
    }
    }

  18. ok I found the solution (so much simpler). Just go to the css file:

    .st_main img.st_preview{
    z-index:-2;
    position:absolute;
    left:0px;
    top:0px;
    width:100%;
    }

    and change “width” by “height”. Done. No more cropping of photos. Now the problem is to center the photos. Any suggestions?

  19. It’s a shame no one cares to answer my questions. I don’t know why I bother posting them and why I bother keeping a link at my website to your page…
    Anyway here’s another question: I checked your demo at IE9 and no problems. I downloaded it and opened it with IE9 locally and all the letters just disappeared. That’s right no heading nor menu’s names.I uploaded it to my website (again, without any changes) to see if the problem had to do with any serverside processing but the problem continued.
    This is all very strange because your demo version works at IE9 and the downloaded version doesn’t. What’s wrong? Anyone experienced the same issue?

  20. Hi there, your demo and the one on the website definitely doesnt correspond, the downloadable demo’s Menu doesnt work properly in IE – In fact, you only see 10px of the menu, nothing more

  21. On my build i have tried to add navigational buttons up down to switch the collections and left right ,for the current collection, to swap the image – they would preferably loop so after the last going to the first – is this possible iv tried blending this tutorial with http://tympanus.net/codrops/2010/09/08/full-page-image-gallery/ but no luck yet!

    Is it possible for the background to be 100% high or 100% wide – so that it fills the browser?

    also can the initial image displayed be randomised? i can do it via a horrible method using http://javascriptsource.com/miscellaneous/random-image.html onto of the actual first image πŸ™ is there any way to embed this function into the initial part of the script – i have tired but no cigar!

    http://p3.phcd.co.uk/

    is where i am now – as of now – still trying i can’t get the navigation buttons at the bottom to work

    if anyone could help me fix the buttons or make the image fill the frame

    i would be so happy πŸ™‚

  22. You are so bad ass! That is the best image gallery I have seen (even vs. the devil Flash).

    I will send you a link up showing my next site using a couple of your tuts.

    Thanks!
    Rob

  23. @Anton
    I found a way to solve our IE9 problem. Edit your index.html and place the line below as the first line after the tag:

    This way, IE9 will by default load the page using it’s compatibility mode and that worked for me.

    @Matthew
    Meanwhile after hundreds of tries, I concluded that I couldn’t center the image. My workaround was setting a left padding, so that the images aren’t covered by the menus and they look as they are centered.

    [css file below…]

    .st_main img.st_preview{
    z-index:-2;
    position:absolute;
    left:150px; /* this value worked for me */
    height:100%; /* this fixes the vertical photos problem */
    }

    To get full page backgrounds, either with horizontal or vertical photos/backgrounds, you just have to substitute “width” by “height” on the css file.

    You can check my progress so far here: http://www.diogonunes.com/photography


    I’m having a new problem, a weirder one: the menus look good at my laptop and on my desktop they get all overlapped, which is weird because the browser is the same… still trying to find the reason for this.

  24. http://www.sirlawrencedavis.com/sld_test/index.html

    I attempted to adapt this so that the entire tab can be used to select the tabs and for some reason the thumbnails flash, or blink whenever they are “hidden”. I have changed the function 50 times over and for the life of me I can’t get that to stop happening. I find that it doesn’t do it if I just make the arrow the only selectable part. Any ideas?

  25. sorry wrong link:
    http://sirlawrencedavis.com/index.html

    I attempted to adapt this so that the entire tab can be used to select the tabs and for some reason the thumbnails flash, or blink whenever they are β€œhidden”. I have changed the function 50 times over and for the life of me I can’t get that to stop happening. I find that it doesn’t do it if I just make the arrow the only selectable part. Any ideas?

  26. sorry the blog wont accept the code so inside each img braket you have to write title=”blablabla” then it ill display on mouve over of the img

  27. Thnks DenisD!
    I’ve tried and yes I can see the title under the mouse, but if I want to have the title to be write like here: http://dl.dropbox.com/u/22933018/SITO/index1.html

    If you watch under “portfolio”, when you move on the pictures you can see the name on it, but it doesn’t move and always appeared on the first photo. I can’t understand why.

    My CSS is:
    .st_thumbs{
    height:166px;
    margin: 0;
    position: absolute;
    }
    .st_thumbs > a{
    display: block;
    }
    .st_thumbs > a > span{
    display: block;
    position: absolute;
    margin-left:auto;
    color:#fff;
    font-size: 28px;
    bottom: -40px;
    left: 18px;
    text-shadow: 5px 1px 1px rgba(0,0,0,0.4);
    -webkit-transition: all 0.1s ease-in-out;
    -moz-transition: all 0.9s ease-in-out;
    -o-transition: all 0.1s ease-in-out;
    -ms-transition: all 0.1s ease-in-out;
    transition: all 0.1s ease-in-out;
    }
    .st_thumbs a:hover > span{
    display: block;
    position: absolute;
    border: 0;
    float:left;
    text-align: left;
    left: 20px;
    bottom: 108px;
    }
    .st_thumbs img{
    border: 0;
    float:left;
    margin:3px 3px 0px 0px;
    cursor:pointer;
    -moz-box-shadow:1px 1px 5px #000;
    -webkit-box-shadow:1px 1px 5px #000;
    box-shadow:1px 1px 5px #000;
    opacity:0.85;
    filter:progid:DXImageTransform.Microsoft.Alpha(opacity=85);
    }

  28. ok…watch my test site, i think is kind of what you want…if you look at the source code you’ll see for each pict you have to set a padding-left, rising from the complete left and add 183 or 186px (not sure iv modify mine). Its a bit exhaustive but it work…at this time i work on formule jquery to count the position by itself but for now this is it.
    http://www.ma117.info/thumbtest/index.html

  29. @DiAlex

    Thanks, it did work for me – I’ve added #meta http-equiv=”X-UA-Compatible” content=”IE=8″# in my header just below the title and it worked – obviously just replace the # with opening and closing tags – the IE=9 thing didnt really work