Try Tuts+ Premium, Get Cash Back!
Image Gallery With Custom Sized Images (Bonus jQuery Plugin)

Image Gallery With Custom Sized Images (Bonus jQuery Plugin)

Tutorial Details
  • Program: WordPress / jQuery
  • Version: 3+ / 1.7.1
  • Difficulty: Medium
  • Estimated Completion Time: 1 hour

As I promised in my previous article, here is the tutorial on creating a gallery out of the custom sized images. As a bonus you’ll learn how to create a basic jQuery plugin to present the larger sized image of the thumbnail in a more appealing way.

If you did not read my previous article about custom image sizes, please read it so it will be easier to understand this tutorial.


Step 1 Decide on Image Sizes

Depending on your theme’s layout, and what grid system it uses, you can decide on different image sizes for thumbnails. Before deciding think about how many images you want to have in a row, what is the width of the container where the thumbnails will be displayed, margins, paddings, etc.

I will use a 940 pixel wide, 2 column layout (8/4 ratio) as an example, where the content will be 620 pixels and the sidebar 300 pixels with a 20 pixel left margin. The content area and the sidebar area will have inner padding of 20 pixels. Now a little math: content is 620px wide with padding of 20px, leaving 580px for thumbnails; and displaying 5 thumbnails per line, each having a right margin of 10px so they are not stuck together; 5th image in each line will not have a right margin; thumbnails will be 108px in width and height, and cropped.

The larger sized image will be maximum of 660px wide and 660px tall, they will be resized proportionally without cropping.

You can choose whatever sizes fit your layout, if you decide to change sizes they can be easily regenerated (see previous post on how to do it), and they don’t have to be square shaped.


Step 2 Setting Up the Custom Sizes

Edit the functions.php file so it looks something like this:

add_action( 'after_setup_theme', 'setup' );
function setup() {
	// ...

	add_theme_support( 'post-thumbnails' );
	add_image_size( 'preview', 108, 108, true ); // thumbnail
	add_image_size( 'zoomed', 660, 600 ); // large

	// ...
}

Step 3 Generate Thumbnail List, Excluding the Image Set as Featured

Still in functions.php add the method generate_thumbnail_list:

function generate_thumbnail_list( $post_id = null ) {
	if ( $post_id == null ) return;
	$images = get_posts(
		array(
			'numberposts' => -1,
			'post_type' => 'attachment',
			'post_mime_type' => 'image/jpeg, image/jpg, image/png, image/gif',
			'post_parent' => $post_id,
			'orderby' => 'menu_order',
			'order' => 'ASC',
			'exclude' => get_post_thumbnail_id( $post_id )
		)
	);
	if ( count( $images ) > 0 ) {
		echo '<ul class="gallery">';
		foreach ( $images as $image ) {
			$src = wp_get_attachment_image_src($image->ID, 'zoomed');
			echo '<li><a href="' . $src[0] .'">' . wp_get_attachment_image( $image->ID, 'preview' ) . '</a></li>';
		}
		echo '</ul>';
		echo '<div class="clear"></div>';
	}
}

In content-single.php call the generate_thumbnail_list method, passing the post ID as a parameter.

<?php the_content(); ?>
<h3>Images</h3>
<?php generate_thumbnail_list( get_the_ID() ); ?>

The above will output an unordered list, containing links to the larger files and the thumbnail images.


Step 4 Styling the Gallery

Obviously the thumbnails need to be styled, otherwise it is just a plain list of images. Add the following to your existing stylesheet or create a new one and enqueue it:

.clear {
	clear: both;
}
.gallery {
	list-style-type: none;
	padding: 0;
	margin: 0;
}
.gallery li {
	float: left;
	margin: 0 10px 10px 0;
}
.gallery li:nth-child(5n) {
	margin-right: 0;
}
.gallery a {
	float: left;
	cursor: pointer;
	text-decoration: none;
}
.gallery img {
	float: left;
	border: 0;
}

This will put images next to each other, leaving some space around them.

At this point clicking on the thumbnail will open the large image on a blank page. This is a good fallback in case the JavaScript is disabled.


Step 5 Open the Images With a jQuery Image Gallery

Style Zoomed Image Gallery

Before writing any JavaScript it would be desirable to know how the large image will be displayed. Here is what I had in mind:

Note: all this will be generated from the jQuery plugin. This is just to show the process of creation.

A transparent overlay on top of all the content, with the image in the middle and the controls on the top right corner. While the image is loading there will be a spinner showing. In the root of the document body a wrapper div will be added, which will contain the links for navigation to the next and previous, a link to close the gallery, and the wrapper around the image where the image will be loaded. This is the minimal HTML code that will be used for the gallery.

<div id="zoom">
	<a href="#next" class="next">Next</a>
	<a href="#previous" class="previous">Previous</a>
	<div class="close"></div>
	<div class="content"></div>
</div>

Adding the following style will style the above elements as in the image above (comments are included to explain parts that may not be immediately obvious):

#zoom {
	z-index: 99990; /* high index so it stays on top of all other elements */
	position: fixed; /* is fixed so if content is scrolled this stays in the same place */
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	/* creates a transparent background, so the content under it will be visible,
	transparency can be adjusted */
	background: rgba(0, 0, 0, 0.8);
}
#zoom .content {
	z-index: 99991; /* higher index so the image will stay on top of the overlay */
	position: absolute;
	/* start initial positioning: will be centered horizontally and vertically */
	top: 50%;
	left: 50%;
	width: 200px;
	height: 200px;
	margin: -100px 0 0 -100px;
	/* end positioning */
	/* an animated spinner as background will be visible while the image is loading */
	background: #ffffff url('../img/spinner.gif') no-repeat 50% 50%;
	border: 20px solid #ececec;
	padding: 0;
}
#zoom img {
	display: block;
	max-width: none;
	background: #ececec;
	-moz-box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3);
	-webkit-box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3);
	box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.4);
}
#zoom .close {
	z-index: 99993; /* higher index so the close will stay above the overlay and image */
	position: absolute;
	top: 0;
	right: 0;
	width: 49px;
	height: 49px;
	cursor: pointer;
	background: transparent url('../img/icons/close.png') no-repeat 50% 50%;
	opacity: 1;
	filter: alpha(opacity=100);
}
#zoom .previous,
#zoom .next {
	z-index: 99992; /* higher index so the close will stay above the overlay and image */
	position: absolute;
	top: 0;
	overflow: hidden;
	display: block;
	width: 49px;
	height: 49px;
	text-indent: 100%;
}
#zoom .previous {
	right: 100px;
	background: url('../img/icons/arrows.png') no-repeat 0 0;
}
#zoom .next {
	right: 50px;
	background: url('../img/icons/arrows.png') no-repeat 100% 0;
}
#zoom .close:hover {
	background-color: #da4f49; /* adds a red tint on hover */
}
#zoom .previous:hover,
#zoom .next:hover {
	background-color: #0088cc; /* adds a blue tint on hover */
}

The outcome of the above:

Now Some JavaScript

The HTML code above will not be needed, it will be generated with JavaScript. Events will be attached for opening, navigating and closing the gallery. Navigating and closing the gallery can be done from the keyboard or using the mouse.

The JavaScript below is also commented to explain what’s going on:

(function($) {
	$.zoom = function() {
		 // append a gallery wrapper to the document body
		$('body').append('<div id="zoom"></div>');
		var zoomedIn = false,  // a flag to know if the gallery is open or not
		    zoom = $('#zoom'),
		    zoomContent = null,
		    opened = null; // the opened image element

		function setup() {
			zoom.hide(); // hide it
			// add the inner elements, image wrapper, close and navigation links
			zoom.prepend('<div class="content"></div>');
			zoom.prepend('<div class="close"></div>');
			zoom.prepend('<a href="#previous" class="previous">Previous</a>');
			zoom.prepend('<a href="#next" class="next">Next</a>');
			
			zoomContent = $('#zoom .content');
			// attach events to the added elements
			$('#zoom .close').on('click', close);
			$('#zoom .previous').on('click', openPrevious);
			$('#zoom .next').on('click', openNext);
			
			// observe keyboard events for navigation and closing the gallery
			$(document).keydown(function(event) {
				if (!opened) {
					return;
				}
				if (event.which == 27) {
					$('#zoom .close').click();
					return;
				}
				if (event.which == 37) {
					$('#zoom .previous').click();
					return;
				}
				if (event.which == 39) {
					$('#zoom .next').click();
					return;
				}
				return;
			});
			
			if ($('.gallery li a').length == 1) {
				// add 'zoom' class for single image so the navigation links will hide
				$('.gallery li a')[0].addClass('zoom');
			}
			// attach click event observer to open the image
			$('.zoom, .gallery li a').on('click', open);
		}
		
		function open(event) {
			event.preventDefault(); // prevent opening a blank page with the image
			var link = $(this),
			    src = link.attr('href'),
				// create an image object with the source from the link
			    image = $(new Image()).attr('src', src).hide();
			if (!src) {
				return;
			}
			$('#zoom .previous, #zoom .next').show();
			if (link.hasClass('zoom')) {
				$('#zoom .previous, #zoom .next').hide();
			}
			
			// show the gallery with loading spinner, navigation and close buttons
			if (!zoomedIn) {
				zoomedIn = true;
				zoom.show();
			}
			
			// clean up and add image object for loading
			zoomContent.empty().prepend(image);
			// event observer for image loading, render() will be
			// called while image is loading
			image.load(render);
			opened = link;
		}
		
		function openPrevious(event) {
			event.preventDefault();
			if (opened.hasClass('zoom')) {
				return;
			}
			var prev = opened.parent('li').prev();
			if (prev.length == 0) {
				prev = $('.gallery li:last-child');
			}
			prev.children('a').trigger('click');
		}
		
		function openNext(event) {
			event.preventDefault();
			if (opened.hasClass('zoom')) {
				return;
			}
			var next = opened.parent('li').next();
			if (next.length == 0) {
				next = $('.gallery li:first-child');
			}
			next.children('a').trigger('click');
		}
		
		function render() {
			// if the image is not fully loaded do nothing
			if (!this.complete) {
				return;
			}
			var image = $(this);
			// if image has the same dimensions as the gallery
			// just show the image don't animate
			if (image.width() == zoomContent.width() && 
			    image.height() == zoomContent.height()) {
				show(image);
				return;
			}
			var borderWidth = parseInt(zoomContent.css('borderLeftWidth'));
			// resize the gallery to the image dimensions before
			// displaying the image
			zoomContent.animate({
				width: image.width(),
				height: image.height(),
				marginTop: -(image.height() / 2) - borderWidth,
				marginLeft: -(image.width() / 2) - borderWidth
			}, 300, function(){
				show(image);
			});

			function show(image) {
				image.fadeIn('fast');
			}
		}
		
		function close(event) {
			event.preventDefault();
			zoomedIn = false;
			zoom.hide();
			zoomContent.empty();
		}
		
		setup();
	};
})(jQuery);

After including the above plugin, initialize it by adding the plugin call into the generate_thumbnail_list method:

function generate_thumbnail_list( $post_id = null ) {
	// ...
	if ( count( $images ) > 0 ) {
		// ...
		echo '<script type="text/javascript">jQuery.zoom();</script>';
	}
}

Example

A real life example of how this works and how it can be used: Zoom jQuery Photo Gallery Plugin demo


References

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • http://www.smartdatasoft.com arifur rahman

    Robert Călin

    This is really great post.We did this kind of work but one trips know from your article that is how to exclude the featured image.

    I have a question. If our post have any image which we didn’t set a gallery. Will we able to got that image also. Or we have to set the image order by a gallery.

    Thanks again for your great article.

    arifur rahman
    smart data soft

    • https://gurde.com Robert
      Author

      @arifur rahman: I didn’t understand your question. Can you please try to explain it better.

  • Al

    I use XP on my older laptop and the downloaded code example will not work with IE8. can this be fixed or does it not work with IE8 and lower?

    I like the way it works in FF and Chrome, but, if it does not work in IE8 and lower, it really becomes difficult to use on any web site I look after, I do need to use something that works on IE8 and lower. not everybody has the latest and greatest software and hardware, certainly not my users anyway.

    Al

    • https://gurde.com Robert
      Author

      I’m working on a fix, but IE6 will not be supported.

      • al

        on the main site I look after, IE8 is the leading browser, followed by IE9, then Safari, Firefox, Chrome and lastly IE7. everything else is not a large percentage of activity, but it does include a small number of IE6 users. I guess it all boils down to what users you want to serve when they come onto your site using whatever browser they decide to use.

        I am quite fine with no support for IE6, my site is not a technical site, it is a site for a curling club so I would not expect a large number of FF and Chrome users and that is what I see.

        thanks for looking into a fix for IE8

        Al

        • https://gurde.com Robert
          Author

          I updated the code, you can download it from my site while the content is being updated here on Tuts+.

          Enjoy :)

  • http://www.duuro.com Perjan

    Great tutorial and plugin man.
    I wonder if you can add sharing functionalities to images on twitter of facebook. Is it possible?

    Thanks

  • WujekGreg

    Hi i got another solution without a function,

    <?
    			global $post;
    			
    			$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
    			$post_per_page = 20;
    			$images = $args=get_children( array(
    			'post_type' => 'produkty',
    			'post_parent' => $post->ID,
    			'post_status' => 'inherit',
    			'products_cat' => array('bochenek','pieczywo'),
    			'posts_per_page' => $post_per_page,
    			'paged' => $paged,
    			'post_mime_type' => 'image'
    			) );
    			$size = 'produkty_thumb';
    			if ($images) {
    				$num_of_images = count($images);
    				$i = 0;
    				foreach ($images as $image) {
    					$img_title = $image->post_title;
    					$img_url = wp_get_attachment_url($image->ID);
    					$preview_array = image_downsize($image->ID, $size );
    					$img_preview = $preview_array[0];
    			$temp = $wp_query;
    			$wp_query = null;
    			$wp_query = new WP_Query($args);
    			while ($wp_query->have_posts()) : $wp_query->the_post(); ?>
    			<div id="prodpost">
    				<div class="thmb"> 
    					<?php echo $img_preview;?>
    						<a class="tip" title="<?php echo $image_title; ?>" href="<?php echo $img_url; ?>"><?php echo $img_preview;?></a>
    				</div>
    				 <span></span>
                 </div>
    			 <?php endwhile; ?>
    
  • Pingback: Фото галерея на WordPress с изображениями заданного размера | Wordpresso

  • Pingback: WordPress Community Roundup for the Week Ending April 28

  • Pingback: 40+ Responsive jQuery Image Gallery Plugins. | Pillaticos

  • http://www.wordpressguru.com.au/ Wordpress Dev

    Thanks Robert,

    This is awesome. Regarding your code, can I use it on my projects or not. Do I have any restrictions using it.

  • Pingback: 7 jQuery Plugins for Thumbnails