Image Wall with jQuery

Today we want to show you how to create a neat image wall with jQuery. The idea is to scatter some thumbnails with different sizes on the page and make […]


Today we want to show you how to create a neat image wall with jQuery. The idea is to scatter some thumbnails with different sizes on the page and make a ribbon slide in when we click on the picture. The ribbon will show some description next to the picture and when clicking again on the thumbnail, the ribbon will close and open again with a large version of the image.

To scatter the images we will be using the CSS3 child selector property and the jQuery Masonry plugin by David DeSandro.

The beautiful photos are by Mark Sebastian and you can see his Flickr photostream here. The images are licensed under the Creative Commons Attribution-ShareAlike 2.0 Generic License.

Let’s start with the HTML structure.

The Markup

The HTML structure is pretty straightforward: we will have a wrapper for our unordered list of images and their descriptions and a ribbon element where we will add a closing span and a help text span:

<div class="iw_wrapper">
	<ul class="iw_thumbs" id="iw_thumbs">
		<li>
			<img src="images/thumbs/1.jpg" data-img="images/full/1.jpg" alt="Thumb1"/>
			<div>
				<h2>Description Heading</h2>
				<p>Some description text...</p>
			</div>
		</li>
		<li>...</li>
		...
	</ul>
</div>
<div id="iw_ribbon" class="iw_ribbon">
	<span class="iw_close"></span>
	<span class="iw_zoom">Click thumb to zoom</span>
</div>

The “data-img” will tell us the path to the full image which will insert dynamically into the ribbon div.
Let’s take a look at the style.

The CSS

First, we will define the style of the wrapper. We want it to be centered on the page and to have a width that will adapt to the window size, so we give it a width of 70%:

.iw_wrapper{
	width:70%;
	margin:30px auto 100px auto;
	position:relative;
}

Since we will be using the Masonry Plugin to minimizes vertical gaps between the images we will have a neat repositioning animation when we i.e. resize the window.

Next, we will define the style for the list elements. (I am assuming that you are including some sort of reset css that will set the margins and paddings of unordered lists to 0).
By default we want all the list elements to have a margin of 5px and to float:

ul.iw_thumbs li{
	float:left;
	margin:5px;
}

The description will be placed absolutely and in our JavaScript function we will decide the left positioning (either it will be -200px in order to be shown left to the image or the width of the image in order to be shown to the right):

ul.iw_thumbs li div{
	position:absolute;
	top:5px;
	width:180px;
	padding:0px 10px;
	display:none;
	color:#fff;
	z-index:100;
}

Let’s style the heading and the text:

ul.iw_thumbs li div h2{
	font-family: 'Wire One', arial, serif;
	font-size:38px;
	text-transform:uppercase;
	text-shadow:0px 0px 1px #fff;
}
ul.iw_thumbs li div p{
	font-size:11px;
	line-height:16px;
	font-style:italic;
}

The images will have a thick white border and some box shadow:

ul.iw_thumbs li img{
	border:7px solid #fff;
	cursor:pointer;
	position:relative;
	-moz-box-shadow:1px 1px 1px #aaa;
	-webkit-box-shadow:1px 1px 1px #aaa;
	box-shadow:1px 1px 1px #aaa;
}
ul.iw_thumbs li img:hover{
	-moz-box-shadow:1px 1px 7px #777;
	-webkit-box-shadow:1px 1px 7px #777;
	box-shadow:1px 1px 7px #777;
}

Now we want to add some chaos to the list elements and the images. We will adjust the margin of some list elements by selecting specific children. We take the first list element and we add a margin-left of 50 px:

ul.iw_thumbs li:nth-child(1){
	margin-left:50px;
}

Then we take all the even list elements and give them a different top margin:

ul.iw_thumbs li:nth-child(even){
	margin-top:30px;
}

All multiples of 3 will have a left margin of 20 px:

ul.iw_thumbs li:nth-child(3n){
	margin-left:20px;
}

Now we will play with the heights of the images. Like that we will give the whole wall a scattered look and avoid making it look like a boring grid. We also want to make the images a little bit smaller or bigger because we want to animate them to the height of the ribbon when we click on them. So this will add an interesting effect, since some images will grow and some will shrink:

ul.iw_thumbs li:nth-child(even) img{
	height:20px;
}
ul.iw_thumbs li:nth-child(odd) img{
	height:40px;
}
ul.iw_thumbs li:nth-child(5n) img{
	height:70px;
}
ul.iw_thumbs li:nth-child(6n) img{
	height:110px;
}
ul.iw_thumbs li:nth-child(7n) img{
	height:20px;
}

The ribbon will be a fixed element that will come sliding out from the left or the right side depending on where the thumbnail is positioned. The width is 0 initially and we will animate it to 100%.

.iw_ribbon{
	position:fixed;
	height:126px;
	width:0px;
	left:0px;
	top:0px; 
	background:#000;
	opacity:0.8;
	z-index:10;
	overflow:hidden;
	display:none;
}

The close and the zoom text will have the following style:

.iw_close{
	position:absolute;
	top:10px;
	right:10px;
	background:#f0f0f0 url(../images/close.gif) no-repeat center center;
	width:18px;
	height:18px;
	display:none;
	cursor:pointer;
}
.iw_zoom{
	color:white;
	font-size:8px;
	font-family:Arial, sans-serif;
	text-transform:uppercase;
	padding:14px;
	display:none;
	float:right;
	margin-right:30px;
}

When we click on a thumbnail while we are in the “ribbon mode”, we want the big image to appear inside of the ribbon and then we’ll expand the ribbon in order to reveal it. So we will dynamically add the image into our ribbon and apply the following style:

.iw_ribbon img{
	position:absolute;
	top:50%;
	left:50%;
	border:7px solid #fff;
}

We want the image to be centered, so we add a top and left of 50% and define the negative margins dynamically once we know the size of the image. The margins need to be the negative half of the width and height of the image.

And finally, we define the loading span style which is an element that we will add dynamically into the list element in order to indicate that the big image is loading:

.iw_loading{
	background: #fff url(../images/loader.gif) no-repeat center center;
	width:28px;
	height:28px;
	position: absolute;
	top: 50%;
	left: 50%;
	z-index: 10000;
	margin: -14px 0px 0px -14px;
	opacity:0.8;
}

And that’s all the style!

Let’s add some juice!

The JavaScript

Let’s first cache some elements and then define our function:

var $iw_thumbs			= $('#iw_thumbs'),
	$iw_ribbon			= $('#iw_ribbon'),
	$iw_ribbon_close	= $iw_ribbon.children('span.iw_close'),
	$iw_ribbon_zoom		= $iw_ribbon.children('span.iw_zoom');

	ImageWall	= (function() {
	 ...
	})();

ImageWall.init();

In our function we will start by defining some variables:

// window width and height
var w_dim,
	// index of current image
	current				= -1,
	isRibbonShown		= false,
	isFullMode			= false,
	// ribbon / images animation settings
	ribbonAnim			= {speed : 500, easing : 'easeOutExpo'},
	imgAnim				= {speed : 400, easing : 'jswing'},

Next, we’ll define the init function which will first call the masonry plugin, calculate the windows dimensions and initialize some events:

	init				= function() {
		$iw_thumbs.imagesLoaded(function(){
			$iw_thumbs.masonry({
				isAnimated	: true
			});
		});
		getWindowsDim();
		initEventsHandler();
	},

“getWindowsDim” will get the dimensions of the window:

getWindowsDim		= function() {
		w_dim = {
			width	: $(window).width(),
			height	: $(window).height()
		};
	},

Then we’ll define the initializations of some events, like the click on the thumbnail image, the closing of the ribbon and the window resize:

	initEventsHandler	= function() {

		// click on a image
		$iw_thumbs.delegate('li', 'click', function() {
			if($iw_ribbon.is(':animated')) return false;

			var $el = $(this);

			if($el.data('ribbon')) {
				showFullImage($el);
			}
			else if(!isRibbonShown) {
				isRibbonShown = true;

				$el.data('ribbon',true);

				// set the current
				current = $el.index();

				showRibbon($el);
			}
		});

		// click ribbon close
		$iw_ribbon_close.bind('click', closeRibbon);

		// on window resize we need to recalculate the window dimentions
		$(window).bind('resize', function() {
					getWindowsDim();
					if($iw_ribbon.is(':animated'))
						return false;
					closeRibbon();
				 })
				 .bind('scroll', function() {
					if($iw_ribbon.is(':animated'))
						return false;
					closeRibbon();
				 });

	},

“showRibbon” will take care of the things that will happen when we show the ribbon:

showRibbon			= function($el) {
		var	$img	= $el.children('img'),
			$descrp	= $img.next();

		// fadeOut all the other images
		$iw_thumbs.children('li').not($el).animate({opacity : 0.2}, imgAnim.speed);

		// increase the image z-index, and set the height to 100px (default height)
		$img.css('z-index', 100)
			.data('originalHeight',$img.height())
			.stop()
			.animate({
				height 		: '100px'
			}, imgAnim.speed, imgAnim.easing);

		// the ribbon will animate from the left or right
		// depending on the position of the image
		var ribbonCssParam 		= {
				top	: $el.offset().top - $(window).scrollTop() - 6 + 'px'
			},
			descriptionCssParam,
			dir;

		if( $el.offset().left < (w_dim.width / 2) ) {
			dir = 'left';
			ribbonCssParam.left 	= 0;
			ribbonCssParam.right 	= 'auto';
		}
		else {
			dir = 'right';
			ribbonCssParam.right 	= 0;
			ribbonCssParam.left 	= 'auto';
		}

		$iw_ribbon.css(ribbonCssParam)
				  .show()
				  .stop()
				  .animate({width : '100%'}, ribbonAnim.speed, ribbonAnim.easing, function() {
						switch(dir) {
							case 'left' :
								descriptionCssParam		= {
									'left' 			: $img.outerWidth(true) + 'px',
									'text-align' 	: 'left'
								};
								break;
							case 'right' :	
								descriptionCssParam		= {
									'left' 			: '-200px',
									'text-align' 	: 'right'
								};
								break;
						};
						$descrp.css(descriptionCssParam).fadeIn();
						// show close button and zoom
						$iw_ribbon_close.show();
						$iw_ribbon_zoom.show();
				  });

	},

Closing the ribbon will either animate the ribbon from one side or “close” it by decreasing its height when we are in full image mode:

	closeRibbon			= function() {
		isRibbonShown 	= false

		$iw_ribbon_close.hide();
		$iw_ribbon_zoom.hide();

		if(!isFullMode) {

			// current wall image
			var $el	 		= $iw_thumbs.children('li').eq(current);

			resetWall($el);

			// slide out ribbon
			$iw_ribbon.stop()
					  .animate({width : '0%'}, ribbonAnim.speed, ribbonAnim.easing); 

		}
		else {
			$iw_ribbon.stop().animate({
				opacity		: 0.8,
				height 		: '0px',
				marginTop	: w_dim.height/2 + 'px' // half of window height
			}, ribbonAnim.speed, function() {
				$iw_ribbon.css({
					'width'		: '0%',
					'height'	: '126px',
					'margin-top': '0px'
				}).children('img').remove();
			});

			isFullMode	= false;
		}
	},

We also need to take care of the other things that are happening when we close the ribbon, like reset the z-index of the current image and fade out the description:

	resetWall			= function($el) {
		var $img		= $el.children('img'),
			$descrp		= $img.next();

		$el.data('ribbon',false);

		// reset the image z-index and height
		$img.css('z-index',1).stop().animate({
			height 		: $img.data('originalHeight')
		}, imgAnim.speed,imgAnim.easing);

		// fadeOut the description
		$descrp.fadeOut();

		// fadeIn all the other images
		$iw_thumbs.children('li').not($el).animate({opacity : 1}, imgAnim.speed);								
	},

Now we’ll define what happens when we want to show the full image:

	showFullImage		= function($el) {
		isFullMode	= true;

		$iw_ribbon_close.hide();

		var	$img	= $el.children('img'),
			large	= $img.data('img'),

			// add a loading span on top of the image
			$loading = $(' ');

		$el.append($loading);

		// preload large image
		$('').load(function() {
			var $largeImage	= $(this);

			$loading.remove();

			$iw_ribbon_zoom.hide();

			resizeImage($largeImage);

			// reset the current image in the wall
			resetWall($el);

			// animate ribbon in and out
			$iw_ribbon.stop().animate({
				opacity		: 1,
				height 		: '0px',
				marginTop	: '63px' // half of ribbons height
			}, ribbonAnim.speed, function() {
				// add the large image to the DOM
				$iw_ribbon.prepend($largeImage);

				$iw_ribbon_close.show();

				$iw_ribbon.animate({
					height 		: '100%',
					marginTop	: '0px',
					top			: '0px'
				}, ribbonAnim.speed);
			});
		}).attr('src',large);

	},

And finally, we will have a resize function that will care of the size of the full image, i.e. we want it to fit into the screen:

	resizeImage			= function($image) {
		var widthMargin		= 100,
			heightMargin 	= 100,

			windowH      	= w_dim.height - heightMargin,
			windowW      	= w_dim.width - widthMargin,
			theImage     	= new Image();

		theImage.src     	= $image.attr("src");

		var imgwidth     	= theImage.width,
			imgheight    	= theImage.height;

		if((imgwidth > windowW) || (imgheight > windowH)) {
			if(imgwidth > imgheight) {
				var newwidth 	= windowW,
					ratio 		= imgwidth / windowW,
					newheight 	= imgheight / ratio;

				theImage.height = newheight;
				theImage.width	= newwidth;

				if(newheight > windowH) {
					var newnewheight 	= windowH,
						newratio 		= newheight/windowH,
						newnewwidth 	= newwidth/newratio;

					theImage.width 		= newnewwidth;
					theImage.height		= newnewheight;
				}
			}
			else {
				var newheight 	= windowH,
					ratio 		= imgheight / windowH,
					newwidth 	= imgwidth / ratio;

				theImage.height = newheight;
				theImage.width	= newwidth;

				if(newwidth > windowW) {
					var newnewwidth 	= windowW,
						newratio 		= newwidth/windowW,
						newnewheight 	= newheight/newratio;

					theImage.height 	= newnewheight;
					theImage.width		= newnewwidth;
				}
			}
		}

		$image.css({
			'width'			: theImage.width + 'px',
			'height'		: theImage.height + 'px',
			'margin-left'	: -theImage.width / 2 + 'px',
			'margin-top'	: -theImage.height / 2 + 'px'
		});							
	};

return {init : init};

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

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 54

Comments are closed.
  1. This is great, I have a use for this, the only thing I would like to add is to ability to hyperlink the large image (after the thumb is clicked) is that possible somehow?

  2. It’s very nice, I think just one feature is missing: clicking an empty area should close bigger images.

  3. Great Stuff… It’s working on internet explorer 8 as well but the css3 isn’t working so it’s a little less beautiful !
    Whatever … thanx for sharing… And I was wondering as you are good with javascript you must know know a good book or website to learn javascript ?

  4. Great script, a navigation (left,right) to change images in full view would be perfect as well as other user said, clicking on the big picture to close it as well for not going always into the small close button.

  5. @César Couto : to close ribbon on ribbon click add :
    $iw_ribbon.bind(‘click’, closeRibbon);
    $iw_ribbon_zoom.bind(‘click’, closeRibbon);
    below :
    $iw_ribbon_close.bind(‘click’, closeRibbon);

  6. Bummer…very buggy in iOS (at least the iPhone). Beautiful though.

  7. Mary, I love you. Long time no hear from you. And you back with another awesome stuff

  8. Awesome gallery and tutorial as usual, thank you!

    If one wanted to have a grid of uniform thumbnail images, how would the CSS be altered? Is it just the ‘.iw_thumbs li:nth-child’ margins and heights that would be changed or would there be a better way? I will experiment but figured I’d ask anyway. Thanks again!

  9. @MaryLou Hi, first of all, thanks for share this stuff!

    Well, I have a question, if I don’t want zoom for images how I can activate or desactivate zoom?

    tks!

  10. very nice, but there’s a problem in Opera. When you first open the page the images strangly overlap each other since you resize the windows, then everything works as it should.

  11. It’s very nice, but I question the useability of it. When I click a thumbnail of an image I expect to see a larger version of that image, not to have to click a second time to view the full picture.

    Maybe there’s scope for creating a nice lightbox that shows the full zoomed photo upon click, with the title/info shown to the left/right of the image.

    /2cents

  12. I think when you zoom in on the large image the information about that image should not go away.

  13. The idea is to scatter some thumbnails with different sizes on the page and make a ribbon slide in when we click on the picture. The ribbon will show some description next to the picture and when clicking again on the thumbnail, the ribbon will close and open again with a large version of the image.

  14. Would be good to see some extension to show how you could import it into say WordPress and how to ensure compatibility with other scripts.

  15. This is fantastic! I downloaded and am currently tricking around with it and was wondering if it’s possible to have the image load full screen from the first click? To have the full screen function initialised from the first click? Thank you!

  16. For IE 7
    Put animated:true;
    Under isAnimated and it will work. Just trial and a whole lot of error.

  17. BEAUTIFUL! How could this be adapted so the ribbon appears in the same area of the page regardless of the location of the thumbnail clicked?

  18. This is elegant, somehow i would like to use it in my website !
    thanks a lot for sharing.

  19. not understanding conflict between jquery versions. in source, for /2.html as comparison, inclusion of 1.4.4/jquery.min.js breaks image wall in favor for parallax effect. any ideas?

  20. Hi, great job.
    I need to emlarge image on the wall on first click. Can please alyone tell me, how to make it?

  21. Hi, thanks really amazing!!
    But I’d like to have the ribbon with the image in the center of the page too. I achieved this – for the ribbon- by setting the ribbonCssParam to “top : (w_dim.height / 5)”- for my image of 531px height. But there seems no chance to make the image in the ribbon centered as well? The (now) enlarged image keeps appearing where its thumb is located. Any help would be very much appreciated cuz the imagewall is amazing!!

  22. Thanks Stef! You’re halfway there. I’ll tinker with the code and see if I can get the desired centered ribbon effect working. Can anyone else lend a hand?

  23. Hi Kid Kilowatt,

    many thanks for your reply and help! Unfortunately I’m a complete noob ;-S it’s a shame…