Sliding Panel Photo Wall Gallery with jQuery

Today we will create a stunning full page photo wall gallery. The idea is to have a whole page full of thumbs with a nice light effect when we hover. […]

Today we will create a stunning full page photo wall gallery. The idea is to have a whole page full of thumbs with a nice light effect when we hover. When an image is clicked, a panel slides up from the bottom revealing the full picture. When clicking on the full image, the thumbs panel slide back from the bottom. This effect will give the impression that we are stacking the panels on top of each other every time we change the mode. In the full picture view we add some nice transition effect when we browse through the photos.

In addition, we will use a function for resizing the full image, adapting it to the size of the screen. So, when the screen get’s resized, our image will adapt automatically!

The beautiful images in the demo are from Vincent Boiteau’s photostream on Flickr.

This tutorial will be a little bit more complex than usual, so I will give my best to explain things clearly.

Let’s get started with the markup.

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 consists of three main containers: one for the info bar at the bottom of the page, one for the thumbnails and one panel container for the full image:

 <div class="infobar">
	<span id="description"></span>
	<span id="loading">Loading Image</span>
</div>
<div id="thumbsWrapper">
	<div id="content" >
		<img src="thumbs/1.JPG" alt="images/1.JPG" title="something"/>
		<img src="thumbs/2.JPG" alt="images/2.JPG" title="something"/>
		...
		<div class="placeholder"></div>
	</div>
</div>
<div id="panel">
	<div id="wrapper">
		<a id="prev"></a>
		<a id="next"></a>
	</div>
</div>

The info bar container has a span for the image description and one for the loading display. The thumbnail wrapper contains a content container for all the small images. The very last element after the thumbnails is a placeholder. We need to add some space to the end because we don’t want the thumbnails to get covered by the info bar. Since we don’t want to use any paddings or margins in our containers, we simply add another filler element.

The structure for the panel contains a wrapper for the full image and two navigation items. In our JavaScript function we will add the full size image to this wrapper.

Now, let’s take a look at the style.

The CSS

First, we will define some general style for the page:

*{
    margin:0;
    padding:0;
}
body{
    background-color:#000;
    font-family:Verdana;
    text-transform:uppercase;
    color:#fff;
    font-size:10px;
    overflow:hidden;
}

The body needs to get the property overflow hidden, because we will set the scroll bar for the thumbs view only. If we would add scrolling to the page, the scroll bar would appear when the full image panel slides up. We avoid that by saying that the overflow will be hidden for the body.

The info bar will have a fixed position. We always want it to be visible and at the same place in the page:

.infobar{
    background-color:#000;
    height:28px;
    line-height:28px;
    right:20px;
    position:fixed;
    bottom:0px;
    left:20px;
    z-index:999999999;
    text-align:center;
    color:#ddd;
    -moz-border-radius:10px 10px 0px 0px;
    -webkit-border-top-left-radius:10px;
    -webkit-border-top-right-radius:10px;
    border-top-left-radius:10px;
    border-top-right-radius:10px;
    text-shadow:0px 0px 1px #ccc;
}

We add some border radius for rounded borders. We set the z-index crazily high since we want the element to be always on top. of course you could just use a value like 15 or 50. Just be careful with the other elements that will get a new z-index dynamically in the JS. The highest one of them is 10.

The description and the loading item will have the following style:

span#description{
    text-shadow:1px 1px 1px #000;
    display:none;
}
span#loading{
    display:none;
    padding-right: 30px;
    background:transparent url(../loading.gif) no-repeat center right;
}

The loading item will have a loading image as background.

The thumbs wrapper will have a panel like style. We set it fixed, occupying the whole screen. Important for the effect is the bottom:0px since we will want the wrapper to come up from the bottom by animating its height. Again, we set the overflow to hidden, since we just want the content container to have a scroll bar.

#thumbsWrapper{
    overflow:hidden;
    position:fixed;
    height:100%;
    width:100%;
    left:0px;
    right:0px;
    bottom:0px;
}

The content wrapper will also occupy all the page. The vertical scroll bar we set explicitly by saying overflow-y:scroll (auto would be the default value). Initially, we want this container to be invisible because we don’t want to show the scroll bar while there is nothing on the page during the initial loading time.

#content{
   position:absolute;
   top:0px;
   height:100%;
   width:100%;
   left:0px;
   background-color:#111;
   overflow-y:scroll;
   display:none;
}

The images in the content container will float left and have an opacity of 0.4. The hover function will then increase opacity, giving the whole thing a spotlight effect.

#content img{
    float:left;
    margin:2px;
    cursor:pointer;
    opacity:0.4;
    filter:progid:DXImageTransform.Microsoft.Alpha(opacity=40);
}

The placeholder will make sure that our thumbs don’t get covered by the info bar when we are at the bottom of the page:

.placeholder{
    float:left;
    clear:both;
    width:100%;
    height:30px;
}

The panel will have the following style similar to the style of the thumbs wrapper:

#panel{
    background-color:#222;
    width:100%;
    position:fixed;
    bottom:0px;
    left:0px;
    right:0px;
    height:0px;
    text-align:center;
}

Initially, the

#panel img{
    cursor:pointer;
    position:relative;
    border:1px solid #000;
    -moz-box-shadow:0px 0px 10px #111;
    -webkit-box-shadow:0px 0px 10px #111;
    box-shadow:0px 0px 10px #111;
    display:none;
}

We set it to display:none since we will make it appear by fading it in. This will create a nice effect.

The image wrapper be centered horizontally. This we achieve by setting the left and right margins to auto.

#wrapper{
    position:relative;
    margin:40px auto 0px auto;
}

The navigation items will have the following style:

a#next,
a#prev{
    width:40px;
    height:40px;
    position:fixed;
    cursor:pointer;
    outline:none;
    display:none;
    background:#aaa url(../nav.png) no-repeat top left;
}
a#next:hover, a#prev:hover{
    background-color:#fff;
}
a#next{
    right:0px;
    top:50%;
    margin-top:-20px;
    background-position: 0px 0px;
}
a#prev{
    left:0px;
    top:50%;
    margin-top:-20px;
    background-position: 0px -40px;
}

We give the navigation items a fixed position. To center an absolute or fixed element horizontally (or vertically) you can give it a top (or left) value of 50% and then a negative top (or left) margin of half of its height (or width). Since the navigation item is 40px high, our top margin is is -20px.

And that was all the style. Now, let’s take a look at the JavaScript magic:

The JavaScript

OK, don’t scare, it’s a bit lengthy… but don’t worry, most of it is comments and code indention 🙂
Our main functions fire when we click on a thumb or click on a full image.

When clicking on a thumbnail (line 34) we first show our loading item in the info bar. Then we say to load the respective image (line 40) with the source being the alt attribute of the thumb we clicked (line 92). Everything that is inside of the load function will just be executed after the image is loaded (line 41 to 93). So, we hide the loading item from the info bar, resize the image to fit into the viewport and append the image element to the wrapper. Then we fade it in (line 54) and slide up the panel where we will see the full image and the fading in effect still executing (57 – 91). We also show the description and the navigation items, and finally we make the thumbs wrapper disappear by setting the height to 0 (line 90). We do that because we want it sliding back from the bottom when we click on the full image.

For the click event on the full image (line 101 to 119) we need to use “live” since the img element is not there in the beginning but it is created dynamically. We animate the thumbs wrapper and set the panel to zero height.

The functions for browsing through the full images check which thumb image is the previous or next one and according to that we set the next or previous full image (line 128 to 137). For that we use the navigate function (line 143 to 191).

From line 18 to 26 we say that we want the full size picture to be resized whenever we resize the window. The resize function is defined from line 197 until 237. We also resize the wrapper around the image.

$(function() {
	/* this is the index of the last clicked picture */
	var current = -1;
	/* number of pictures */
	var totalpictures = $('#content img').size();
	/* speed to animate the panel and the thumbs wrapper */
	var speed 	= 500;

	/* show the content */
	$('#content').show();

	/*
	when the user resizes the browser window,
	the size of the picture being viewed is recalculated;
	 */
	$(window).bind('resize', function() {
		var $picture = $('#wrapper').find('img');
		resize($picture);
	});

	/*
	when hovering a thumb, animate it's opacity
	for a cool effect;
	when clicking on it, we load the corresponding large image;
	the source of the large image is stored as
	the "alt" attribute of the thumb image
	 */
	$('#content > img').hover(function () {
		var $this   = $(this);
		$this.stop().animate({'opacity':'1.0'},200);
	},function () {
		var $this   = $(this);
		$this.stop().animate({'opacity':'0.4'},200);
	}).bind('click',function(){
		var $this   = $(this);

		/* shows the loading icon */
		$('#loading').show();

		$('').load(function(){
			$('#loading').hide();
			if($('#wrapper').find('img').length) return;
			current 	= $this.index();
			var $theImage   = $(this);
			/*
			After it's loaded we hide the loading icon
			and resize the image, given the window size;
			then we append the image to the wrapper
			*/
			resize($theImage);

			$('#wrapper').append($theImage);
			/* make its opacity animate */
			$theImage.fadeIn(800);

			/* and finally slide up the panel */
			$('#panel').animate({'height':'100%'},speed,function(){
				/*
				if the picture has a description,
				it's stored in the title attribute of the thumb;
				show it if it's not empty
				 */
				var title = $this.attr('title');
				if(title != '')
					$('#description').html(title).show();
				else
					$('#description').empty().hide();

				/*
				if our picture is the first one,
				don't show the "previous button"
				for the slideshow navigation;
				if our picture is the last one,
				don't show the "next button"
				for the slideshow navigation
				 */
				if(current==0)
					$('#prev').hide();
				else
					$('#prev').fadeIn();
				if(current==parseInt(totalpictures-1))
					$('#next').hide();
				else
					$('#next').fadeIn();
				/*
				we set the z-index and height of the thumbs wrapper
				to 0, because we want to slide it up afterwards,
				when the user clicks the large image
				 */
				$('#thumbsWrapper').css({'z-index':'0','height':'0px'});
			});
		}).attr('src', $this.attr('alt'));
	});

	/*
	when hovering a large image,
	we want to slide up the thumbs wrapper again,
	and reset the panel (like it was initially);
	this includes removing the large image element
	 */
	$('#wrapper > img').live('click',function(){
		$this = $(this);
		$('#description').empty().hide();

		$('#thumbsWrapper').css('z-index','10')
		.stop()
		.animate({'height':'100%'},speed,function(){
			var $theWrapper = $(this);
			$('#panel').css('height','0px');
			$theWrapper.css('z-index','0');
			/*
			remove the large image element
			and the navigation buttons
			 */
			$this.remove();
			$('#prev').hide();
			$('#next').hide();
		});
	});

	/*
	when we are viewing a large image,
	if we navigate to the right/left we need to know
	which image is the corresponding neighbour.
	we know the index of the current picture (current),
	so we can easily get to the neighbour:
	 */
	$('#next').bind('click',function(){
		var $this           = $(this);
		var $nextimage 		= $('#content img:nth-child('+parseInt(current+2)+')');
		navigate($nextimage,'right');
	});
	$('#prev').bind('click',function(){
		var $this           = $(this);
		var $previmage 		= $('#content img:nth-child('+parseInt(current)+')');
		navigate($previmage,'left');
	});

	/*
	given the next or previous image to show,
	and the direction, it loads a new image in the panel.
	 */
	function navigate($nextimage,dir){
		/*
		if we are at the end/beginning
		then there's no next/previous
		 */
		if(dir=='left' && current==0)
			return;
		if(dir=='right' && current==parseInt(totalpictures-1))
			return;
		$('#loading').show();
		$('').load(function(){
			var $theImage = $(this);
			$('#loading').hide();
			$('#description').empty().fadeOut();

			$('#wrapper img').stop().fadeOut(500,function(){
				var $this = $(this);

				$this.remove();
				resize($theImage);

				$('#wrapper').append($theImage.show());
				$theImage.stop().fadeIn(800);

				var title = $nextimage.attr('title');
				if(title != ''){
					$('#description').html(title).show();
				}
				else
					$('#description').empty().hide();

				if(current==0)
					$('#prev').hide();
				else
					$('#prev').show();
				if(current==parseInt(totalpictures-1))
					$('#next').hide();
				else
					$('#next').show();
			});
			/*
			increase or decrease the current variable
			 */
			if(dir=='right')
				++current;
			else if(dir=='left')
				--current;
		}).attr('src', $nextimage.attr('alt'));
	}

	/*
	resizes an image given the window size,
	considering the margin values
	 */
	function resize($image){
		var windowH      = $(window).height()-100;
		var windowW      = $(window).width()-80;
		var theImage     = new Image();
		theImage.src     = $image.attr("src");
		var imgwidth     = theImage.width;
		var imgheight    = theImage.height;

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

For further details, check the comments in the code, they describe what is done in the moment.

And that’s it! I hope you enjoyed the tutorial and the result!

Message from TestkingLearn useful web design applications with testking ccna course and become expert using testking ccnp design tutorials and testking ccie design recourses.

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 117

Comments are closed.
  1. This is why I have to come back to this site on weekly (if not daily) basis. Great post and thank you very much!

  2. I think I, and others, would pay for this if it pulled flickr photos automatically along with the photo description. Beautifully done.

  3. Great work ! I think I’ll use it for my portfolio. 🙂

    There is something that disturb me in your code. It’s your usage of “alt”, it should be used to provide a description of the picture. Maybe there is a nicer way to implement this … I just think about sightless people.

  4. Is there a way to include a tooltip(i.e “return to gallery” or “exit fullscreen”) once you’ve clicked on a thumbnail and brought the panel up?
    Great post, I’ve really enjoy the tutorial – thanks!

  5. Thank you so much for this. I found out your tutorial mostly involving jQuery/javscript which is absolutely fantastic. Hope to see more from you. I learned a lot btw.

  6. Love this, THANKS!

    And I am sure I’ll figure it out eventually… but has anyone been able to make the ‘photo wall’ contained?

    I’m using a grid framework and have managed to shrink my photo set down – but alas, due to fixed positioning in the css ~ I cannot get the set to be locked into a container.

    I’ll keep at it and hopefully have success soon… but wanted to ask in case anyone else is trying to NOT have this full-screen. Thanks again!

  7. Great gallery, I love it!

    One quiestion though: is it possible to incorporate a logo on the left side of the description of the images, and make the descriptionbox square to the right side of the logo, thus assuring the text will not flow over the logo?

    Pretty badly written there, but I guess you understand it…

  8. Hi! I’m working on merging together your wall gallery and sliding thumbnails gallery as a project to learn JQuery on my own. I’m pretty happy with the outcome, but I’m having some debugging issues, and think it may have to do with a line of code I don’t understand in your source: On #40 and then again on #153, you use the following JQuery:

    $(”).load(…

    I can’t find in the JQuery documentation of .load a sample of selecting a set of elements using a tag enclosed in

    Can you tell me what this $(”) is doing, or point me to some documentation that uses this format?

    Any info would be super helpful.

  9. My bug had nothing to do with that line of code. Also, I have figured out what the code does. 🙂

    Thanks for the great codebase!

  10. Luv it… but large image could do with a tooltip onhover so viewers know to click it to go back 🙂

  11. if you decide to center this dont forget to resize the placeholder. You may not have to but i set all my thumbs to 100px and centered the page within a fixed width div and the bottom row was covered up. I set the height to 125px in the placeholder and it worked perfectly. Thanks again for posting this. I love the minimalist look.

  12. hi, is it possible to link a thumbnail to a web page (html or php) instead of a larger image??
    coz i would like to have a photo album with comment box on each image…

    thanks~~~~

  13. Hi!
    Great gallery! I like it a lot but I have a problem with making the thumbnails have the same size. I was thinking of putting the images in divs and set thumbnails as centered background, for example:

    Could You help ?
    Thanks

  14. Mary Lou it doesn’t seem to work in ie8, any work around for that. When you click a thumb the thumbs don’t slide down and a lager image appears over the thumbs.

  15. Hi, great script! Im trying to get into this and i have a question: my index page consists of 6 thumbs, and i want to center them at all times (so also with different screen resolutions). Ive tried everything, but it wont seem to work. I tried lining them up with the left-margin/right-margin commands, etc… Large images show fine at different screen resolutions.

    Is there a way to get this problem fixed? Is there anyone who knows the answer to this problem?

    Thanks!

  16. Hi.. just having a play with your sliding wall full page gallery.. It looks great.. however for some reason my landscape pictures are becoming the size of portrait pics and vice versa.. so both look strectched .. I cannot work out why this is.. Any ideas?

  17. Is there anyway to get this effect without hard coding the image files into the code?

    Perhaps have it look for all images in a folder to populate the thumbnails?

  18. Hi, I have used this script as a starting point for my photo wall memorial website, I have put a link on my links page back to this tutorial. Many thanks

  19. Thanks a lot for this awesome tut !
    Just one question : do you think it’s possible to do a preloader for each image ?

  20. Hi! Thank you very much for your good idea&work. I want to ask for small pictures how can i align them horizontally in full view? And in thumbnail view when i changed content img float to center, images horizontal margin garbled.
    And a suggestion: a different version can be 100% opacity and grayscale or different effect on hover images. Because some visitors couldn’t see low opacity in screen very well.

  21. Incase anybody was curious about getting all their images in a specific directory imported into this jquery script, i’ve found a simple way.

    Using php, here’s a lil code snippet i wrote up:
    <?php
    //here, we'll use the php variable $imagedir to specify which directory we want to be working with in this case it will be the "Gothams" sub directory under the root "images" directory
    $imagedir = "../images/Gotham/";

    //here, we grab all images that contain a .png extension, in the case you want to specify which type of image to output.
    $gothimages = glob("" . $imagedir . "*.png");

    //Here we pring each filename and make $gothimages, $gothimage. ($gothimage now holds all the .png images within the selected directory.)
    foreach($gothimages as $gothimage)
    {
    echo "\n”;
    }
    ?>
    For those not experienced in php, you’ll just need to add this code where you will normally add the images and then save the file with a .php extension. As it is now it has the .html extension. so instead of index.html it will be index.php

    If you have any further questions feel free to mail me at michael@lyricalz.com also an example of mine is located here:
    http://www.lyricalz.com/graphicz

  22. Made a mistake there. Here’s correct version:

    <?php
    //here, we'll use the php variable $imagedir to specify which directory we want to be working with in this case it will be the "Gothams" sub directory under the root "images" directory
    $imagedir = "../images/Gotham/";

    //here, we grab all images that contain a .png extension, in the case you want to specify which type of image to output.
    $gothimages = glob("" . $imagedir . "*.png");

    //Here we pring each filename and make $gothimages, $gothimage. ($gothimage now holds all the .png images within the selected directory.)
    foreach($gothimages as $gothimage)
    {
    echo "\n”;
    }
    ?>

  23. Is there is a reason why the this slideshow is bugy on Google Chrome. Everything works fine on Firefox and other browsers but onn Google Chrome randomly images just does not load, I get blank screen.

  24. I’m looking to close the image opened (if any) and return the site back to normal (like a regular view of the thumbs) for is someone was to click on a i.e. menu .. Home for example.. any ideas?

    I’ve been playing with the function to hide the photo but each time I either close the photo and not the panel or both but the thumbs disappear.. =\

  25. Wow. This is really impressive.

    And the fact that you just let other people use this. I mean, wow.

    Mary, people like you make the internet a much much better place.

    Kudos!

  26. Great gallery.

    I have a small query, how do I have two thumbnail content galleries on the same page?

    Say I wanted twice on the page with their own thumbs… but the script currently keeps reading all the thumbs on the page, but I want it to only display images that belong to its parent div only when fullscreen mode.

    Any idea anyone?

  27. great plugin, however, like others i’m having difficulty implementing a close link instead of clicking the large image. I’m just not sure what $this is in the function $(‘#wrapper > img’).live(‘click’,function(){…

    any help would be appreciated.

  28. doesn’t seem to work in Firefox 5 (Mac). Clicking on a photo does not slide up a panel, it puts up a full screen slideshow view of a single photo with arrows. The only way to get back to the wall is to go Back in the browser (i.e., press the Back button)

  29. To add a close link that simulates a click on the large picture to this awesome plugin…

    to the div id=”wrapper” add an anchor tag
    close

    Style it up in the css with

    #wrapper #closeButton {
    position: fixed;
    top: 10px;
    right: 10px;
    display: none;
    cursor: pointer;
    }

    Script its functionality…
    in the $(‘#panel’).animate(…); area add the following: (line 308 of the download)

    $(‘#closeButton’).fadeIn();

    in the $(‘#wrapper > img).live(…); area add the following after $this.remove();….
    $(‘#closeButton’).hide();

    after the $(‘#prev’).bind(…) function.
    add a click handler for the close anchor as follows…

    $(‘#closeButton’).bind(‘click’, function(){
    $(‘#wrapper > img’).click(); /* simulates click on large photo */

    });

  30. Really Nice Work but i have Just one question : do you think it’s possible to do a preloaded for each image ?

  31. Mary Lou, Great Gallery! Really loved it and thanks for sharing. I’m trying to used it in my project and the only problem I have is using a lot of images (279). I lowered the “var speed” from 500 to 10 and still no love. Any ideas on how to resolve the loading speed issue?