jQuery fading and sliding image gallery
March 4th, 2008
The idea was raised by a collegue how to go about recreating something similar to The New York Times The Little Rock Nine: 50 Years Later flash feature without using flash. In my “infinite wisdom” I said something along the lines of,“no problem, jQuery and CSS can do all that”. Good going Alan. After a few days on and off fiddling with it, I may have come across a decent substitute.
My idea began trying to combine the article on jQuery “slicker show and hide” and Apple™ style slider gallery. From there I had to add other effects to the mix so I dug into the jQuery cycle plugin. Those three combined with a little edits and a lot of reading and trial and error, brought me a nice gallery. Take a look at the demo.
This next part is my modifications to my gallery.js file.
Well the first thing I needed to do was make the scroller go vertical instead of horizontal. Modifying the slider code I came up with this:
var container = $('div.sliderGallery');
var ul = $('ul', container);
var itemsHeight = ul.innerHeight() - container.outerHeight();
$('.slider', container).slider({
minValue: 0,
maxValue: itemsHeight,
handle: '.handle',
stop: function (event, ui) {
ul.animate({'top' : ui.value * -1}, 500);
},
slide: function (event, ui) {
ul.css('top', ui.value * -1);
}
});
Essentially changing any reference of Width to Height and any left to top. That seems to make things work. The only issues I’m having, that I noticed, with the slider come in Opera and Safari. In Opera, when you let go of the scrollbar, it loses functionality. You can still click, but you can’t grab. I think this might be in the ui.mouse.js or the ui.slider.js rather than in the little bit of code here. In Safari the dragging is choppy. Not a huge issue and doesn’t break functionality.
// fade page in
$('#load').hide();
$('#load2').fadeTo(2500, 1, function () {
$('#load2').fadeOut(700, function() {
$('#cover').fadeTo(1000, 0);
$('#cover').hide(400);
});
});
Now I’m getting fancy. #load is the image loader gif so I want it to hide. Then I want the circular page loading gif to fade in. In this case in 2½ seconds it fades form transparent to fully opaque then starts a function. It fades out in .7 seconds and starts yet another function that fades out my #cover (all that is is a whiite div that covers the whole page), and after 1 second hides the ##cover. Hiding it is essential because if it’s still there but transparent, you can see what’s underneath but it’s all unclickable. That simultes the page loading effect.
This section tells the first list element to have the active state and load up the first box.
// this sets the first li and the first slickbox as 'on' at load
$('.items li').eq(0).addClass('active');
$('#load').fadeIn(800);
$('#load').fadeTo(800, 1);
$('#load').fadeOut(800, function() {
$('.slickbox').eq(0).fadeIn(800);
$('span').css('opacity', '1');
$('.slickbox').eq(0).prepend('<div class="nav">');
$('.z1').eq(0)
.cycle({
fx: 'fade',
speed: 500,
timeout: 0,
pager: '.nav'
});
});
Since count starts at 0, the eq:(0) tells jQuery to find the very first of each of these elements. in the following section I’ll explain what all this is doing as they are very similar. The above is just specifically calling the first instances.
This part took me the longest to figure out.
// fade and toggle boxes and content
$(".items li").each(function (i){
$('.slickbox').hide();
$('#load').hide();
var $match = $('.slickbox').eq(i);
var $match2 = $('.z1').eq(i);
$(this).click(
function (){
$('.nav').remove();
$('.slickbox').fadeOut(200, 0);
$('#load').fadeIn(800);
$('#load').fadeTo(800, 1);
$('#load').fadeOut(800, function() {
$match.fadeIn(800);
$('span').css('opacity', '1');
$match.prepend('<div class="nav">');
$match2
// commented to try out 'prepend' method
//.before('<div class="nav">')
.cycle({
fx: 'fade',
speed: 500,
timeout: 0,
pager: '.nav'
});
});
}
)
});
I take each ,items li in the document and apply a function to them. I want to hide all my .slickbox divs and the image loading gif. The I set up some variables. $match is equal to each .slickbox in order as they come in the document. $match2 is every .z1 in order. $(this).click applies to each list item and starts another functon. $(’.nav’).remove() is real important. Without it, everytime the same list item is clicked it adds another .nav, not good. A second clicks adds one and so on. Doing this confuses the script and makes all the links unclickable, so I want to remove any instance of it from the code. Now I want to fade out any existing .slickbox. If it’s the first time you click it doesn’t do anything but this keeps multiple boxes from over lapping on the screen. Now my image loading gif fades in then stays on the screen for 1 second then fades out and fires yet another function. $match(i) fades in. The i will be the number in order as it is in the document and will be the same number as the list you click. So clicking the third list item on the page loads the third box on the page. The next line, $(’span’).css(’opacity’, ‘1′); is again very important. It took me a wile to figure out why it was not working right.
The situation was that when you first clicked on a list item, it worked fine. At anytime, if you clicked ona previously clicked list item, the first image was not loading. Using Aardvark, I could tell the span and the iamge was actually loading but they were staying transparent. What the previous line does is manually resests the span to opaque. It seems jQuery doesn’t do this, or doesn’t do this with this release.
Then I start the clycle plugin to set up a pager and make the contents of the span act as a gallery.
The last of this is the easiest part really. Adding and removing classes on the list items as they are clicked and/or hovered over.
// add-remove class on lists
$(".items li").addClass('notactive');
$('.items li').click(function() {
$(this).addClass('active');
if ($(this).is('active')) {
$(this).removeClass('active');
} else {
$('.items li').removeClass('active');
$(this).addClass('active');
}
});
// hover the lists
$('.items li').hover(function() {
$(this).addClass('activeh');
}, function() {
$(this).removeClass('activeh');
});
// remove border onfocus
$('.items li').click(function() {
$(this).removeClass('activeh');
});
The removing the border on focus is not needed actually. At first I had links over the whole list item and the border was causing visual issues.
The HTML is really straight forward. The CSS is a little messy as you have to define and redefine classes being set and removed by jQuery. Since this was an experiement in jQuery I’m not going to go into too much detail about either. The one thing that will be a factor if you use this is that my example has a fixed height and width. This make the images in the gallery have to be a max height or width as well. A lot of absolute positioning is also used so take special care when altering the CSS. There are some elements that have background colors applied to them, namely the span and h2 of the ul.items. This has to be set in order to fix the font rendering bug in IE6. There is also a bug I’ve only noticed in IE6. When you hover over a non-active list item, it removes the border. I think chaining three classes in the css freaks IE6 out. I might have to look into applying the border change via javascript.
Special thanks go out to:
- The New York Times
- malsup’s jQuery cycle plugin
- jQuery for Designers’ Slider Gallery
- Learning jQuery’s Slicker Show and Hide
- Dans Blog, Mathing Paired Elements
Known issues
- .handle “locking” in Opera
- notactive hover mis-handling the class addition in IE6
Futre possible additions
- Make the slider scale depending on the maxvalue of the UL
- make a height variable and add the slider spans height trough jQuery.
- Make loading of one box die if another list item is clicked.
*Note: This is just a test for my learning. It is not supposed to work without javascript enabled. I did not go through the steps to make it do so as I am learning jQuery first. If at some point I want to implement this gallery, I will try and find a suitable non-javascript alternative as well.
Demo looks nice.
Posted by: Licht on May 1st, 2008
Great demo - thank you. One question — when the number of images within each “Gallery” exceeds 10 (I should say 12 to be more definite) the excess are not displayed by the page. I am looking into a “safe-guard” to wrap into the jQuery call that would go to “next line” or “additional” in another area. If you have any ideas please let me know.
Posted by: Dustin on August 12th, 2008