Creating a jQuery Slideshow of the WordPress Attachments Gallery
Since version 2.5 WordPress has included the ability to upload images and “attach” them to posts and pages. The user can upload a batch of images and the system will create copies at various sizes specified in the site settings. There’s an interface for setting an order and captions. It’s good, and I decided to make use of it for a site I’ve been working on.
The image set is normally included in the post by typing the shortcode in the post body. But in this case I didn’t want my users to have to bother with that — I wanted to call the gallery directly in the theme. And then I wanted to display the images using a simple sliding interface. Unlike my previous slideshow, this one needed to support many instances on a single page.
See an example of the jQuery in action | Download a ZIP of all the files
Insert the Attachment Gallery via the Theme
As helpfully explained in the WordPress codex, it’s very easy to call the gallery from the theme:
<?php echo do_shortcode('[gallery option1="value1"]'); ?> |
But I wasn’t satisfied with the HTML this outputs; I wanted to create my own, which meant adding a function. For this I am indebted to Dameian Lott’s WordPress Image Attachment Gallery (Revisited) post. His method has a lot of useful features and is meant to be really flexible so if you’re going to be doing customization I urge you to head over to his site, grab the full code and read his explanation.
I stripped it down to only the parts I needed. Specifically, I’m outputting the “large” size images, wrapped in certain markup:
Note: This function has been revisited in the post Showing Images from the WordPress Attachments Gallery. Use it instead!
function marty_get_images($size = 'large', $limit = '0', $offset = '0', $post_id = '$post->ID') { global $post; $images = get_children( array('post_parent' => $post_id, 'post_status' => 'inherit', 'post_type' => 'attachment', 'post_mime_type' => 'image', 'order' => 'ASC', 'orderby' => 'menu_order ID') ); if ($images) { echo '<div class="gallery">'; $num_of_images = count($images); if ($offset > 0) : $start = $offset--; else : $start = 0; endif; if ($limit > 0) : $stop = $limit+$start; else : $stop = $num_of_images; endif; $i = 0; foreach ($images as $attachment_id => $image) { if ($start <= $i and $i < $stop) { $img_title = $image->post_title; // title. $img_description = $image->post_content; // description. $img_caption = $image->post_excerpt; // caption. $img_alt = get_post_meta($attachment_id, '_wp_attachment_image_alt', true); if ($img_alt == '') { $img_alt = $img_title; } if ($size == 'large') { $big_array = image_downsize( $image->ID, $size ); $img_url = $big_array[0]; // large. } else { $img_url = wp_get_attachment_url($image->ID); // url of the full size image. } ?> <div class="image"><p><img src="<?php echo $img_url; ?>" alt="<?php echo $img_alt; ?>" title="<?php echo $img_title; ?>" /></p></div> <?php if ($img_caption != '') : ?> <div class="attachment-caption"><?php echo $img_caption; ?></div> <?php endif; ?> <?php if ($img_description != '') : ?> <div class="attachment-description"><?php echo $img_description; ?></div> <?php endif; } $i++; } } echo '</div><!-- End gallery -->'; } |
And then I call it in my theme like so:
<?php marty_get_images('large','0','0',"$post->ID"); ?> |
The jQuery Slideshow
Before getting into it, I want to say that there are a number of things about this code that are unresolved, and even as I go through writing these notes I’m seeing duplication that ought to be refactored/abstracted out. But I’m posting it here so as to assist others who, like me, are still learning this stuff. Any suggestions you have to improve this code are very welcome!
OK, first the CSS. I like to put as much of the CSS as possible into my stylesheet (as opposed to inserting it on the fly).
.gallerywrapper {width: 700px; overflow: hidden; } .gallerywrapper .image {width:700px; float: left; } .gallerywrapper .image p {margin-bottom: 0;} .gallerywrapper .slidecontrols {clear: both; width: 100%; overflow: hidden; font-size: 11px; margin-top: 0;} .gallerywrapper .slidecontrols a {color: #ccc; font-weight: bold; font-size: 110%;} .gallerywrapper .slidecontrols a:hover {text-decoration: none;} .gallerywrapper .slidecontrols a.active {color: #666;} .gallerywrapper .slidecontrols a.active:hover {cursor: pointer; text-decoration: underline;} |
As you can see, my slides are set to 700px wide. That’s specific to my case and you will want to change that. The best solution would be to re-write the code so that it’s dynamic to whatever context it’s applied to, and down the road I’ll do that.
On to the jQuery! First off we’ll detect the width of our first slide, as we’ll need this variable for calculations later on.
var slideWidth = $('.image:eq(0)').width(); |
Presenting images in a slideshow requires some elements (such as controls) that aren’t relevant when the images simply follow each other on the page. Because we want our non-JavaScript visitors to be served only the code they need, we left these elements out of the markup we generated above. Here we’ll inject that mark-up which is specific to the slideshow:
// When there's more than one slide, act on the Gallery div $('.image:not(:only-child)').parent().each(function(index) { // Count the number of slides imageLength = $(this).find('.image').length; // Wrap the whole Gallery in a div. $(this).wrap('<div class="gallerywrapper" />'); // Set the Gallery div to the combined width of all the slides $(this).css('width', slideWidth * imageLength); // Inject the controls $(this).parent().append('<p class="slidecontrols"><a title="Previous" class="prev"><span>←</span></a> <a title="Next" class="next"><span>→</span></a> <span class="counter" /></p>'); // Add the slide count to the controls $(this).parent().find('.slidecontrols').append('/' + imageLength); // Determine which instance of the gallery we're working with thisWrapper = $('.gallerywrapper').index($(this).closest('.gallerywrapper')); // Run the Manager method, which is explained below manageGallery(0, thisWrapper); }); // The listener on the controls $('.slidecontrols a').click(function(event) { // Determine which instance of Gallery we're working with thisWrapper = $('.gallerywrapper').index($(this).closest('.gallerywrapper')); // Detect the left-margin of the Gallery... currentMargin = parseInt($(this).closest('.gallerywrapper').find('.gallery').css('marginLeft'), 10); // ... and use it to determine which slide is showing currentPosition = currentMargin / -slideWidth; // Set the target position of the slideshow, determined by which link was clicked nextPosition = ($(this).attr('title')=='Next') ? currentPosition+1 : currentPosition-1; // Run the gallery manager manageGallery(nextPosition, thisWrapper); }); // The listener on the image // This is similar to the above. It would probably be possible/better to combine them into one, reducing duplication. $('.image').click(function(event) { thisWrapper = $('.gallerywrapper').index($(this).closest('.gallerywrapper')); currentMargin = parseInt($(this).closest('.gallerywrapper').find('.gallery').css('marginLeft'), 10); currentPosition = currentMargin / -slideWidth; // Here we're counting the total number of slides... maxPosition = $(this).closest('.gallerywrapper').find('.image').length -1; // ... and setting the slideshow to advance by one unless we're at the end, in which case return to the beginning nextPosition = currentPosition < maxPosition ? currentPosition+1 : 0 ; manageGallery(nextPosition, thisWrapper); }); // And, finally, the Manager method function manageGallery(position, index){ // Here again we're detecting the number of slides maxPosition = $('.gallerywrapper:eq('+index+')').find('.image').length -1; // Create shorthands for the elements we're going to manipulate next = $('.gallerywrapper:eq('+index+')').find('.next'); prev = $('.gallerywrapper:eq('+index+')').find('.prev'); gallery = $('.gallerywrapper:eq('+index+')').find('.gallery'); counter = $('.gallerywrapper:eq('+index+') .slidecontrols .counter'); // This is for displaying the current slide number to the user displayposition = position + 1; // If the target position is within range... if (position <= maxPosition && position >= 0) { // do the slide... $(gallery).animate({ 'marginLeft' : 700 * (-position) } ); // and update the display counter. $(counter).html(displayposition); // If we're at the last slide, grey out the "Next" link position == maxPosition ? $(next).removeClass('active') : $(next).addClass('active'); // And if we're at the first slide, grey out the "Previous" link position == 0 ? $(prev).removeClass('active') : $(prev).addClass('active'); } } |
That’s it! There’s no doubt this could be greatly improved, but I hope seeing it helps you in your project.
Posted April 2011