Thumbnail Grid with Expanding Preview

A tutorial on how to create a thumbnail grid with an expanding image preview similar to the effect seen on Google Images.

Thumbnail Grid with Expanding Preview

If you have searched images on Google recently, you might have noticed the interesting expanding preview for a larger image when you click on a thumbnail. It’s a really nice effect and it is very practical, making a search much easier. Today we want to show you how to create a similar effect on a thumbnail grid. The idea is to open a preview when clicking on a thumbnail and to show a larger image and some other content like a title, a description and a link.

The interesting part is to calculate the correct preview height and to scroll the page to the right position. We’ll expand the preview in a way so that we can see the respective thumbnail row and cover the rest of the remaining page. Note that we don’t use very large images for the preview in the demo so you might see a lot of empty space on large monitors.

The demo features some amazing artwork by Jaime Martinez.

So let’s start!

The Markup

Initially, we need a thumbnail grid for which we will use an unordered list. Each list item will contain an anchor which will have several data attributes:

<ul id="og-grid" class="og-grid">
	<li>
		<a href="http://cargocollective.com/jaimemartinez/" data-largesrc="images/1.jpg" data-title="Azuki bean" data-description="Swiss chard pumpkin bunya nuts maize plantain aubergine napa cabbage soko coriander sweet pepper water spinach winter purslane shallot tigernut lentil beetroot.">
			<img src="images/thumbs/1.jpg" alt="img01"/>
		</a>
	</li>
	<li>
		<a href="http://cargocollective.com/jaimemartinez/" data-largesrc="images/2.jpg" data-title="Veggies sunt bona vobis" data-description="Komatsuna prairie turnip wattle seed artichoke mustard horseradish taro rutabaga ricebean carrot black-eyed pea turnip greens beetroot yarrow watercress kombu.">
			<img src="images/thumbs/2.jpg" alt="img02"/>
		</a>
	</li>
	<li><!-- ... --></li>
	<!-- ... -->
</ul>

The href value will be used to construct the link in the preview description (this also comes in handy when JavaScript is disabled). The data-largesrc attribute contains the path to the larger image. data-title and data-description contain the title and description, respectively.

When we click on a thumbnail, we want a preview element to appear under the list item. For that we will need to insert an element into the grid. In fact, we will use the list item itself and add the preview element after the anchor:

<li>

	<a href="http://cargocollective.com/jaimemartinez/" data-largesrc="images/2.jpg" data-title="Veggies sunt bona vobis" data-description="Komatsuna prairie turnip wattle seed artichoke mustard horseradish taro rutabaga ricebean carrot black-eyed pea turnip greens beetroot yarrow watercress kombu.">
		<img src="images/thumbs/2.jpg" alt="img02"/>
	</a>
	
	<div class="og-expander">
		<div class="og-expander-inner">
			<span class="og-close"></span>
			<div class="og-fullimg">
				<div class="og-loading"></div>
				<img src="images/2.jpg">
			</div>
			<div class="og-details">
				<h3>Veggies sunt bona vobis</h3>
				<p>Komatsuna prairie turnip wattle seed artichoke mustard horseradish taro rutabaga ricebean carrot black-eyed pea turnip greens beetroot yarrow watercress kombu.</p>
				<a href="http://cargocollective.com/jaimemartinez/">Visit website</a>
			</div>
		</div>
	</div>

</li>

Let’s style everything!

The CSS

Note that the CSS will not contain any vendor prefixes, but you will find them in the files.

So, let’s start with the thumbnail grid. It will be full width and we’ll center the text. In this case this will mean that it will center the thumbnails because we’ll set them to display: inline-block:

.og-grid {
	list-style: none;
	padding: 20px 0;
	margin: 0 auto;
	text-align: center;
	width: 100%;
}

.og-grid li {
	display: inline-block;
	margin: 10px 5px 0 5px;
	vertical-align: top;
	height: 250px;
}

The links and images will be displayed as block elements and we’ll remove some default styling:

.og-grid li > a,
.og-grid li > a img {
	border: none;
	outline: none;
	display: block;
	position: relative;
}

When we click on an item, we will give a special class to the respective list item which will be called og-expanded. We’ll add a little arrow as pseudo-element to the anchor:

.og-grid li.og-expanded > a::after {
	top: auto;
	border: solid transparent;
	content: " ";
	height: 0;
	width: 0;
	position: absolute;
	pointer-events: none;
	border-bottom-color: #ddd;
	border-width: 15px;
	left: 50%;
	margin: -20px 0 0 -15px;
}

The preview itself will have the class og-expander and we’ll position that element absolutely. The initial height of the preview will be 0 and we’ll set the overflow to hidden:

.og-expander {
	position: absolute;
	background: #ddd;
	top: auto;
	left: 0;
	width: 100%;
	margin-top: 10px;
	text-align: left;
	height: 0;
	overflow: hidden;
}

.og-expander-inner {
	padding: 50px 30px;
	height: 100%;
}

The inner division will have some paddings and a height of 100%.

The cross for closing the preview will be created using pseudo-elements, i.e. two rotated lines:

.og-close {
	position: absolute;
	width: 40px;
	height: 40px;
	top: 20px;
	right: 20px;
	cursor: pointer;
}

.og-close::before,
.og-close::after {
	content: '';
	position: absolute;
	width: 100%;
	top: 50%;
	height: 1px;
	background: #888;
	transform: rotate(45deg);
}

.og-close::after {
	transform: rotate(-45deg);
}

.og-close:hover::before,
.og-close:hover::after {
	background: #333;
}

The wrappers for the image and for the details will be 50% wide and we’ll make them float next to each other:

.og-fullimg,
.og-details {
	width: 50%;
	float: left;
	height: 100%;
	overflow: hidden;
	position: relative;
}

The details wrapper will have some padding and we’ll center the image inside of the image wrapper by setting the text-align to center and the image itself to display: inline-block. The image will also have a max-height and max-width of 100% so that it adjusts its size to the surrounding container:

.og-details {
	padding: 0 40px 0 20px;
}

.og-fullimg {
	text-align: center;
}

.og-fullimg img {
	display: inline-block;
	max-height: 100%;
	max-width: 100%;
}

Let’s style the text elements and the link:

.og-details h3 {
	font-weight: 300;
	font-size: 52px;
	padding: 40px 0 10px;
	margin-bottom: 10px;
}

.og-details p {
	font-weight: 400;
	font-size: 16px;
	line-height: 22px;
	color: #999;
}

.og-details a {
	font-weight: 700;
	font-size: 16px;
	color: #333;
	text-transform: uppercase;
	letter-spacing: 2px;
	padding: 10px 20px;
	border: 3px solid #333;
	display: inline-block;
	margin: 30px 0 0;
	outline: none;
}

.og-details a::before {
	content: '2192';
	display: inline-block;
	margin-right: 10px;
}

.og-details a:hover {
	border-color: #999;
	color: #999;
}

The loading element will be in the same container as the image and we’ll not use any images but this CSS-only technique. We’ll create a little circle and set three box shadows: one for making the circle itself look a bit smoother and two for “copying” the element. Then we create an animation that will change the background color and the box shadow colors sequentially:

.og-loading {
	width: 20px;
	height: 20px;
	border-radius: 50%;
	background: #ddd;
	box-shadow: 0 0 1px #ccc, 15px 30px 1px #ccc, -15px 30px 1px #ccc;
	position: absolute;
	top: 50%;
	left: 50%;
	margin: -25px 0 0 -25px;
	animation: loader 0.5s infinite ease-in-out both;
}

@keyframes loader {
	0% { background: #ddd; }
	33% { background: #ccc; box-shadow: 0 0 1px #ccc, 15px 30px 1px #ccc, -15px 30px 1px #ddd; }
	66% { background: #ccc; box-shadow: 0 0 1px #ccc, 15px 30px 1px #ddd, -15px 30px 1px #ccc; }
}

Last, but not least, we’ll add two media queries for adjusting the text a bit and for hiding the full image once the screen gets so small that the preview image is not really useful anymore (we’ll also not load it in the JavaScript then).


@media screen and (max-width: 830px) {

	.og-expander h3 { font-size: 32px; }
	.og-expander p { font-size: 13px; }
	.og-expander a { font-size: 12px; }

}

@media screen and (max-width: 650px) {

	.og-fullimg { display: none; }
	.og-details { float: none; width: 100%; }
	
}

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

The JavaScript

Let’s start by caching some elements and initializing some variables:

	// list of items
var $grid = $( '#og-grid' ),
	// the items
	$items = $grid.children( 'li' ),
	// current expanded item´s index
	current = -1,
	// position (top) of the expanded item
	// used to know if the preview will expand in a different row
	previewPos = -1,
	// extra amount of pixels to scroll the window
	scrollExtra = 0,
	// extra margin when expanded (between the preview element and the next item row)
	marginExpanded = 10,
	$window = $( window ), winsize,
	$body = $( 'html, body' ),
	// transitionend events
	transEndEventNames = {
		'WebkitTransition' : 'webkitTransitionEnd',
		'MozTransition' : 'transitionend',
		'OTransition' : 'oTransitionEnd',
		'msTransition' : 'MSTransitionEnd',
		'transition' : 'transitionend'
	},
	transEndEventName = transEndEventNames[ Modernizr.prefixed( 'transition' ) ],
	// support for csstransitions
	support = Modernizr.csstransitions,
	// default settings
	settings = {
		minHeight : 500,
		speed : 350,
		easing : 'ease'
	};

We will start by preloading all the images (thumbnails) in the grid. We then save the offset top and height for each item in the grid, get the current window’s size, and initialize some events:

	function init( config ) {
		
		// the settings..
		settings = $.extend( true, {}, settings, config );

		// preload all images
		$grid.imagesLoaded( function() {

			// save item´s size and offset
			saveItemInfo( true );
			// get window´s size
			getWinSize();
			// initialize some events
			initEvents();

		} );

	}

	// saves the item´s offset top and height (if saveheight is true)
	function saveItemInfo( saveheight ) {
		$items.each( function() {
			var $item = $( this );
			$item.data( 'offsetTop', $item.offset().top );
			if( saveheight ) {
				$item.data( 'height', $item.height() );
			}
		} );
	}

	function getWinSize() {
		winsize = { width : $window.width(), height : $window.height() };
	}

We will bind the click event for each item (anchor) and for the close button (when the item is opened). When we click on an item, the preview with the large image source and the details will be revealed or hidden if already shown. If we click the close button (cross) on the preview then this preview will be closed too.
We are also binding the resize event for the window, where some values are reset and the preview gets closed (if opened).

	function initEvents() {
		
		// when clicking an item, show the preview with the item´s info and large image;
		// close the item if already expanded.
		// also close if clicking on the item´s cross
		$items.on( 'click', 'span.og-close', function() {
			hidePreview();
			return false;
		} ).children( 'a' ).on( 'click', function(e) {

			var $item = $( this ).parent();
			// check if item already opened
			current === $item.index() ? hidePreview() : showPreview( $item );
			return false;

		} );

		// on window resize get the window´s size again
		// reset some values..
		$window.on( 'debouncedresize', function() {
			
			scrollExtra = 0;
			previewPos = -1;
			// save item´s offset
			saveItemInfo();
			getWinSize();
			var preview = $.data( this, 'preview' );
			if( typeof preview != 'undefined' ) {
				hidePreview();
			}

		} );

	}

With the showPreview function we will basically initialize the Preview object, which in turn will expand and reveal the details and the large version of the image. If a Preview instance is already initialized then we will only update the preview with the new details (if the clicked item is in the same row as the current expanded item) or hide it and initialize / open a new one (if not in the same row).
In order to check if the items are in the same row as the current preview, we use the offset top value of the items.

	function showPreview( $item ) {

		var preview = $.data( this, 'preview' ),
			// item´s offset top
			position = $item.data( 'offsetTop' );

		scrollExtra = 0;

		// if a preview exists and previewPos is different (different row) from item´s top, then close it
		if( typeof preview != 'undefined' ) {

			// not in the same row
			if( previewPos !== position ) {
				// if position > previewPos then we need to take the current preview´s height in consideration when scrolling the window
				if( position > previewPos ) {
					scrollExtra = preview.height;
				}
				hidePreview();
			}
			// same row
			else {
				preview.update( $item );
				return false;
			}
			
		}

		// update previewPos
		previewPos = position;
		// initialize new preview for the clicked item
		preview = $.data( this, 'preview', new Preview( $item ) );
		// expand preview overlay
		preview.open();

	}

The Preview object will have a reference to the currently displayed item (Preview.$item), and the index of the expanded item (Preview.expandedIdx). Note that the expanded item is not necessarily the displayed item. For instance if we click on a second item that is on the same row as the one clicked before then the Preview will be “reused” and the Preview.expandedIdx will not be the index of the Preview.$item. We need to keep the reference to the expanded item so that when the Preview is closed we know which item to “collapse”.

	// the preview obj / overlay
	function Preview( $item ) {
		this.$item = $item;
		this.expandedIdx = this.$item.index();
		this.create();
		this.update();
	}

As the Preview object is initialized we create the necessary structure where the item´s details will be rendered and we append it to the item:

	create : function() {
		// create Preview structure:
		this.$title = $( '<h3></h3>' );
		this.$description = $( '<p></p>' );
		this.$href = $( '<a href="#">Visit website</a>' );
		this.$details = $( '<div class="og-details"></div>' ).append( this.$title, this.$description, this.$href );
		this.$loading = $( '<div class="og-loading"></div>' );
		this.$fullimage = $( '<div class="og-fullimg"></div>' ).append( this.$loading );
		this.$closePreview = $( '<span class="og-close"></span>' );
		this.$previewInner = $( '<div class="og-expander-inner"></div>' ).append( this.$closePreview, this.$fullimage, this.$details );
		this.$previewEl = $( '<div class="og-expander"></div>' ).append( this.$previewInner );
		// append preview element to the item
		this.$item.append( this.getEl() );
		// set the transitions for the preview and the item
		if( support ) {
			this.setTransition();
		}
	}

Then we fill the previous structure with the item´s details (stored in data attributes and the href).
The update function will also be used to just update the content of an existing preview.

	update : function( $item ) {

		// update with new item´s details 
		if( $item ) {
			this.$item = $item;
		}
		
		// if already expanded, remove class "og-expanded" from current item and add it to new item
		if( current !== -1 ) {
			var $currentItem = $items.eq( current );
			$currentItem.removeClass( 'og-expanded' );
			this.$item.addClass( 'og-expanded' );
			// position the preview correctly
			this.positionPreview();
		}

		// update current value
		current = this.$item.index();

		// update preview´s content
		var $itemEl = this.$item.children( 'a' ),
			eldata = {
				href : $itemEl.attr( 'href' ),
				largesrc : $itemEl.data( 'largesrc' ),
				title : $itemEl.data( 'title' ),
				description : $itemEl.data( 'description' )
			};

		this.$title.html( eldata.title );
		this.$description.html( eldata.description );
		this.$href.attr( 'href', eldata.href );

		var self = this;
		
		// remove the current image in the preview
		if( typeof self.$largeImg != 'undefined' ) {
			self.$largeImg.remove();
		}

		// preload large image and add it to the preview
		// for smaller screens we don´t display the large image (the last media query will hide the wrapper of the image)
		if( self.$fullimage.is( ':visible' ) ) {
			this.$loading.show();
			$( '<img/>' ).load( function() {
				self.$loading.hide();
				self.$largeImg = $( this ).fadeIn( 350 );
				self.$fullimage.append( self.$largeImg );
			} ).attr( 'src', eldata.largesrc );	
		}

	}

To reveal the preview we need to set the height of the Preview element and also of the item (to push down the items below). The height of the preview will be the window´s height minus the height of the grid item. To avoid cases where that height could be too small we add the option “minHeight” where we can specify the minimum height needed for the preview element.
As the preview opens we will want to scroll the window so that the preview is completely visible (and if possible, also the item).

	open : function() {

		setTimeout( $.proxy( function() {	
			// set the height for the preview and the item
			this.setHeights();
			// scroll to position the preview in the right place
			this.positionPreview();
		}, this ), 25 );

	}

	setHeights : function() {

		var self = this,
			onEndFn = function() {
				if( support ) {
					self.$item.off( transEndEventName );
				}
				self.$item.addClass( 'og-expanded' );
			};

		this.calcHeight();
		this.$previewEl.css( 'height', this.height );
		this.$item.css( 'height', this.itemHeight ).on( transEndEventName, onEndFn );

		if( !support ) {
			onEndFn.call();
		}

	}

	calcHeight : function() {

		var heightPreview = winsize.height - this.$item.data( 'height' ) - marginExpanded,
			itemHeight = winsize.height;

		if( heightPreview < settings.minHeight ) {
			heightPreview = settings.minHeight;
			itemHeight = settings.minHeight + this.$item.data( 'height' ) + marginExpanded;
		}

		this.height = heightPreview;
		this.itemHeight = itemHeight;

	}

	positionPreview : function() {

		// scroll page
		// case 1 : preview height + item height fits in window´s height
		// case 2 : preview height + item height does not fit in window´s height and preview height is smaller than window´s height
		// case 3 : preview height + item height does not fit in window´s height and preview height is bigger than window´s height
		var position = this.$item.data( 'offsetTop' ),
			previewOffsetT = this.$previewEl.offset().top - scrollExtra,
			scrollVal = this.height + this.$item.data( 'height' ) + marginExpanded <= winsize.height ? position : this.height < winsize.height ? previewOffsetT - ( winsize.height - this.height ) : previewOffsetT;
		
		$body.animate( { scrollTop : scrollVal }, settings.speed );

	}

When closing the preview we reset the heights of the preview element and the expanded item. Once this is done, the preview element / structure gets removed from the DOM.

	close : function() {

		var self = this,
			onEndFn = function() {
				if( support ) {
					$( this ).off( transEndEventName );
				}
				self.$item.removeClass( 'og-expanded' );
				self.$previewEl.remove();
			};

		setTimeout( $.proxy( function() {

			if( typeof this.$largeImg !== 'undefined' ) {
				this.$largeImg.fadeOut( 'fast' );
			}
			this.$previewEl.css( 'height', 0 );
			// the current expanded item (might be different from this.$item)
			var $expandedItem = $items.eq( this.expandedIdx );
			$expandedItem.css( 'height', $expandedItem.data( 'height' ) ).on( transEndEventName, onEndFn );

			if( !support ) {
				onEndFn.call();
			}

		}, this ), 25 );
		
		return false;

	}

And that's it! We hope you enjoyed this tutorial and find it inspiring!

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 477

Comments are closed.
  1. Can someone provide IE8 fix. I am not a developer and I want to use this for my photography hobby.
    Much appreciate your help. thanks

  2. I’ve found the fix to the IE problem with the “X” being a “-” when the image is enlarged.

    Add these two lines to – Component.css in the og-close::before,after

    -ms-transform: rotate(45deg); /* Internet Explorer */
    -o-transform: rotate(45deg); /* Opera */

    And similary, these two to og-close::after

    -ms-transform: rotate(-45deg); /* Internet Explorer */
    -o-transform: rotate(-45deg); /* Opera */

  3. Although IE support can be important, I have read more than one comment here saying that without these plugins are useless, and suggesting that the majority of web users are using IE. This isn’t true, according to W3’s latest figures google chrome has the majority of users with almost 30%
    in fact only about 8% of people still use IE8. Added to this if you make some reasonable assumptions about these users you can conclude that they probably aren’t the sort of people that are going to be wanting to view your flashy new portfolios.

    You don’t need full browser support for IE8 and below, what you should aim for is good degradation in these older browsers so that your website will still work, and look alright, even if it doesn’t have 100% of it’s features.

    Sorry that came off as a bit of a rant, just my 2 pence

  4. The main idea is quite nice,
    but I would rather see a sleek album-slideshow than what you guys call a “full-img”.
    isn’t work so well on iPad.

  5. Does anyone know how to add a line break to the text in the expanded view?
    (In the data-description=”” element)

    • I tried using the html for line break – ” ” – inside the ‘data-description’ and this worked fine for me.

  6. Since the launch of new iTunes which pioneered this effect and later Google Image search, I was really inspired by it and trying to implement it myself, I was not even getting close to it. Thanks for sharing and making life easier. This made my day. 🙂

  7. Hi, the full images seems to load too slow from normal. Is there something wrong or no?, if there is can someone tell me how to increase the loading time of the images. ( and yes my images are only 100kb less )

  8. Hello,

    Thanks a lot! Awasome website!!!!!

    I’m encountering a problem when I append some list item to the unordered . All the events doesn’t fire.

    See below the code that I’m using to dynamically add :

    function loadImage(){
    for(i=0;i<3;i++){
    $('#itemContainer').append('‘);
    }
    }

    $(document).ready(function() {
    loadImage();
    Grid.init();
    });

    Have you any suggestion that can help me understand why the onclick event is not handled?

    Thanks.

  9. Hi, to navigate through the pictures, I need to add next and prev links…

    Can you help?

  10. Beautiful job!

    Just one doubt: Everytime that I resize my window, the preview div closes. The same thing happens if I change my iPad from portrait view to landscape view, the preview div closes. So, I wonder if this is how it works.

    Anyway, I just wanted you to know that.

    Thank you.

  11. Beautifull work ! Can I use it, please ? Pretty please ? Just a tiny question: how about IE8 ?

  12. how can I do when I have 1000 images?
    I would like displayed the pictures by javascript, like this:
    $(‘#og-grid’).append(‘ ‘);
    Help please.

  13. I have been trying to alter this slightly so that the popup doesn’t slide down but simple appears over the tiles. I have it appearing over, but the other tiles still slide down. I’ve tried to alter a few different functions but each thing I alter effects a different part of the script. What’s the best way to remove the expanding action without changing anything else?

  14. I think, that will be look like a portfolio for websites well. Of course, capabilities is more, but portfolio… That’s it for this script.

  15. Wow ! It’s beautiful.

    Is that possible to use it without “data-largesrc”, “data-title” and “data-description” ? Because I want to try it for my forum and it’s incompatible with my variables (member’s messages with link or html).

    Thank you !
    (I’m still learning JS, so I can’t understand everything)

    • I figured out how to change data-title and data-description, but it didn´t work with largesrc.
      Go to grid.js and search the word title. There you´ll find all the places where it appers.

      I changed title with cliente
      You´ll fin in grid.js
      cliente : $itemEl.data( ‘cliente’ ),
      this.$cliente.html( eldata.cliente );
      this.$cliente = $( ” );
      this.$details = $( ” ).append( this.$cliente, this.$rubro, this.$detalle, this.$href );

  16. Hi,

    Thank you very much for this tutorial.
    I would like to know if it is possible to play a video file instead of the image

    Regards,

    Will

  17. Hi,

    Thank you for the tutorial.

    I would like the link anchor is also dynamic. how can I do?

    thx

  18. A few people have mentioned IE7 / IE8 support… replacing the jquery.debouncedresize.js function at the start of grid.js with jquery.throttledresize.js found at the GitHub page here seems to fix the expander issue.

  19. Awesome Tutorial! I love you Mary, can i add you as my friend in Facebook? I’m willing to follow you.

  20. Great tutorial. Really beautiful layout.

    Has anyone found a way to stop the expanded windows auto calapsing on mobile devices? Namely Safari/Chrome on iOS iPad, iPhone.
    You open the thumbnail and the content displays for 1 second then auto minimises once more.
    Would love a way to make it function as on the Desktop.

    • Same problem here. Sometimes it stays opened, sometimes the expanded part just auto-collapses. Everything fine on WinPhone8 and Android. I think that should be an easy fix for Mary Lou:)

  21. Very nice animation, easily reusable

    I also have troubles in IE8, where the Preview appears then disappears.

  22. awesome tutorial. quick question, let’s say i wanted to remove or edit the “visit website” part that is in the description of each image, how would i do that?

  23. hi,

    I thought I already post a message fex days ago, but it’s not here, something must have been wrong. o I post again.

    Thanks for sharing this script. I easily managed to adapt it to my website by following line by line the tutorial…
    The only thing I didn’t manage to adapt is the height of the preview: my images are smaller than the ones in the example, so the preview generates a lot of empty space under.
    Is there a way to fix a max height so that it fits to my images ?

    Thanks again,

    janmarie

  24. hi! i try to integrateI have tried to integrate this beautiful script on my website, I need it to work with ie8 but the gray navigation bar is not maintained when making the hover. Do any arrangement or bugfix for IE8 with this functionality?

    Jeanmarie thank you very much!

  25. I’m trying to get the grid to work with an image-map rather than thumb nail images… I’ve played around with it quite a bit but can’t seem to work it out. Any suggestions?

  26. Awesome tutorial! Just a quick question, How do I prevent from it moving everything to the top? I have a floating menu so I need the content to just stay where it’s at.

    • did this get an answer, i have the same issue and its driving me nuts!!!!

    • Hi, im trying to do the same developing my portfolio in wordpress, im having troubles with the loop, do you have the solution ?

  27. Is it possible to implement this grid on WordPress?
    Nice tutorial by the way! Love it

  28. Is there a way to include multiple thumbnail grids on a single page? When I try to do so, only the first thumbnail grids maintain functionality; the rest remain static when the thumbnails are clicked and only function as basic image links.

  29. how do i custom “VISIT WEBSITE” text into several different texts?,,
    because actually i want to include the web title, so the link would be VISIT ‘WEB TITLE’ , something like VISIT CODROPS..

    • Hi sipo,

      It’s easy to change “VISIT WEBSITE” to something else in the .js file, but you can’t have different labels for different items in the grid. So I just use “MORE” and let the user find out where it goes to when they click.

  30. Hi bitfidelity, did you find out how to include multiple thumbnail grids on a single page? If so I’m very interested to hear how you did it? Thanks

  31. Very Great tutorial, a real powerful gallery. Just a question. Is there a way to insert multiple images within the area that opensby click thumbs?

  32. Hi guys.

    Good one.

    Only one question. Could you give me some light in why my data-title and data-description are not showing up? Everything else is okay, but only those two have issue.

    Cheers.

    • In the default.css file have some styles need to use for title & description not showing issues.

      *,
      *:after,
      *:before {
      -webkit-box-sizing: border-box;
      -moz-box-sizing: border-box;
      box-sizing: border-box;
      padding: 0;
      margin: 0;
      }

  33. Excellent grid Mary Lou, apart for just that one thing about IE8, and – worse yet – the iPad! :/

    I’ve tried playing around with it here: Betty test and everything looked good…until I tried on IE8, and on the iPad.
    It looks nice and and the expander opens fine, but closes automatically again, almost immediately…

    I tried to test the demo posted here as well in IE8 and on the iPad, with the same result, so it doesn’t look like the problem is in my code (for once :))

    If anyone could help, I’d appreciate it greatly, cause I really like the grid!

    /imag

    • Thanks a million, Kent – you’re a life saver 🙂

      I completely missed that other post, but updated the grid.js with debounced instead of throttled, and it did the trick – even in IE7!

      And again multiple thanks to Mary Lou for yet another excellent job here on Codrops 😉

    • Great tutorial. I have used it in one of my websites. But I am having a issue when closing the expanded space. I get a massive white space between each row. I have tried the code suggested above (GitHub) and still doesn’t work. It doesn’t work in Chrome or firefox, so it isn’t just IE.

      Help would be much appreciated 🙂

  34. Hi!

    Amazing script. Is there a way to add an HREF to the fullimage? I’ve been scratching my head. I’d like the large image to link out to a URL. Only one URL, so I can hardcode it to work for all fullimages.

    much thanks!

  35. Nice formatting there 🙂
    There should of course have been an A HREF tag surrounding the default DIV inside the ( ” )

    Anyways, hope you get the point…and that it works 🙂

  36. Hi Manoela, as always Codrops produces detail oriented functional UI / UX concepts, this grid layout is really very helpful in combination with sorting categories or multiple grid blocks on a page or a full-width-responsive-slider maybe.
    Would definitely like to try fusion with royalslider script.

    Thanks for sharing a beautiful code and brilliant mind!

    @anandvip

  37. How can I add HREF’s to the fullimages in HTML?
    I’d like the large images to link out to different URLs (vimeo videos).

    I’ve disable the href in HTML :
    – Put href =”#”

    And changed the grid.js, around line 365,
    – From this:
    this.$title.html( eldata.title );
    this.$description.html( eldata.description );
    this.$href.attr( ‘href’, eldata.href );

    – To this :
    this.$title.html( eldata.title );
    this.$description.html( eldata.description );

    if(eldata.href == ‘#’)
    {
    this.$href.hide();
    }
    else
    {
    this.$href.show();
    this.$href.attr( ‘href’, eldata.href );
    }

    Now, how should I modify the HTML to create links from the fullimages?

    • ops, I just saw this post now.
      I am looking for the same thing – sorry for the duplicate post beneath :/

      also tried the anchor, didn’t work.

  38. I would love to be able to use this as a sell item layout but I can’t figure out how to incorporate a paypal button where the “Visit Website” link is located?

  39. Question : I am considering added this plug-in to my website.
    Curious, anyone knows how to alter the code to allow for full sized images? I see a lot of people are wondering as well. Potentially being able to integrate fancybox or lightbox with the code?

    If anyone is successful, this would be greatly appreciated,
    Cheers

  40. Hi! Nicely done! super cool tutorial.

    Here’s one for you:

    – any idea how to share a link with the opened preview?
    the difference in my code is that I don’t need external href for the <a> so I tried using a basic anchor, surely that doesn’t work.
    I see google can do it, so we can do it too 🙂

    anyways, nice work! thanks for sharing this!

  41. I absolutely love this design and have spent some time adapting it for use in my online coaching practice. For example: http://gusgriffin.net/grid/index.html

    I’ve only been testing the webpage in the current version of Google Chrome so far – I know it has problems with other browsers – swf logo not displaying is one – but I’ll address those later. Fortunately, in my business, I can require a client to use a particular browser if necessary. :o)

    As you can see, even though you can’t use HTML on the text in the expander, you can still do quite a bit of text formatting just using and HTML entities (symbols) rather than tags.

    Can anyone suggest how to separate the VISIT WEBSITE button (in my example, I’ve renamed it MORE) so it can be moved out of the ‘og-details’ area? I’d like to move it to the left below a reduced ‘og-fullimg’ area, so I have more room for text in the ‘og-details’ area.

    Also does anyone know how to make the expander scrollable – specifically give the ‘og-details’ area a scroll-bar (but only if needed) – so that more text can be fitted in as necessary?

    All the best, Gus

  42. Has anyone tried to get this to work via ajax?? been trying for good couple of weeks just cant seem to get it to play ball 🙁 if anyone has ideas that would be so good.

    • We’re having this same issue. We’re wanting to load an Ajax page into the expanded area when the thumbnail is clicked. Our content is being generated dynamically, and there’s a lot of it… so we’re definitely needing there to be an external page. We figured out how to load the ajax page inside of the expander, but the only way that we’ve been able to get this to work is by clearing out all of the other elements from the original expander. Which isn’t ideal because it wipes out the close button.

      Cmdv… I’m not sure if this helps, but we commented out everything from here…
      Preview.prototype = {
      create : function() {

      down to hear…
      if( support ) {

      and then added in this to get a page loading with ajax. (This was put in between the two pieces of code listed above.)
      this.$previewEl = $( ” );
      this.$previewEl.load(‘page_name.html’);
      this.$item.append( this.getEl() );

      If you figure out the rest, after the ajax has been loaded, would you please post about it! Thanks!!

    • Hi..Does any one found out how? I came out with this script but doesnt work..

      Preview.prototype = { create : function() { // create Preview structure: var post_url = $(this).attr("href"); var post_id = $(this).attr("rel"); this.$loading = $( '<div class="loading">Loading...</div>' ); this.$previewEl = $( '<div class="og-expander"></div>' ).append( this.$loading ); this.$previewEl.load('post_url'); this.$item.append( this.getEl() ); // set the transitions for the preview and the item if( support ) { this.setTransition(); } }, update : function( $item ) { if( $item ) { this.$item = $item; } // if already expanded remove class "og-expanded" from current item and add it to new item if( current !== -1 ) { var $currentItem = $items.eq( current ); $currentItem.removeClass( 'og-expanded' ); this.$item.addClass( 'og-expanded' ); // position the preview correctly this.positionPreview(); } // update current value current = this.$item.index(); // update preview´s content var $itemEl = this.$item.children( 'a' ), eldata = { href : $itemEl.attr( 'href' ), rel : $itemEl.attr( 'rel' ), }; this.$href.attr( 'href', eldata.href ); this.$rel.attr( 'rel', eldata.rel ); var self = this; },