Slideshow Loading Problems Solved in jQuery Cycle

When it comes to JavaScript components that act on elements of the page, it is highly important to have those elements in place before the script tries to manipulate them, else things won’t work as intended. So, that begs the question, “What’s the best way to have JavaScript run on my page?” In this example we’ll focus on handling javascript while making slideshows with the jQuery Cycle plugin.

At any rate we want our pages to be seen by the visitor right away. We know if it takes more than a couple of seconds for anything to appear on the screen, the visitor is likely to click away to another site. Remember, text appears quickly when it’s not part of a table. The whole table has to be ready before you’ll see the table, so don’t use tables for design purposes, especially nested tables. Save your tables for presenting data.

A problem with big pages having slideshows is that if the page is not ready when a script tries to manipulate things, the effects may be all wrong. For example, when a script calculates the center position for an image it may get the positioning wrong, especially if all the images in the slideshow are not of the same size.

So, how do we improve our slideshow for the masses? We need to tell the javascript when to do its magic. In this example we’ll use this approach:

  1. Perform initial actions with the .ready() method. Actions in this case are to hide images with the .hide() method until they are called for by the script.
  2. Run the slideshow after the content has loaded with the .load() method.
  3. Use a function to calculate the center for each image.
  4. Apply the function with the before option in jQuery Cycle.

A previous post on centering slideshows with jQuery Cycle put all the javascript inside a .ready() statement. For many scripts this would be just fine, but for the slideshow it resulted in the action starting up before all the images were available.

For this example the slideshow is marked up as a group of images inside a <div> that has been assigned an id for JS targeting, #big_show, and a class for CSS targeting, .photos.

HTML Markup:

[plain]

one
two
three
four

[/plain]

Hide Images While Page Loads

Because it may take a while to load up all the photos for the slideshow, and everything else on the page, hide the photos except for the first one to start. Use a .ready() statement to hide the photos because we want to do that right away. Put everything in the .ready() method that you want to run first, even before the rest of the page or content has loaded. The .ready() function is a jQuery construct that allows you to bring functionality to the page before all the content has loaded. That way a page with lot of images can be useful even before all the images are visible.

For hiding images use the .ready() function so that this task will occur as soon as possible.

[crayon]
$(document).ready(function() {
$(‘#s1 img:gt(0)’).hide(); // hides all images with index greater than 0, so it shows the first image only – in one JS/jQuery call

$(‘#s2 img’).hide(); // hides all images
$(‘#s2 img:first’).show(); // shows the first image

$(‘#s3 img’).hide(); // hides all images
$(‘#s3 img:eq(0)’).show(); // shows the image with index equal to 0
});
[/crayon]

Many selectors could be used to target the first element in a series and the example above shows three ways of doing so for three different slideshows, #s1, #s2 and #s3. The outcome of each is the same in that only the first image is shown until the rest of the slideshow is loaded.

Control Slideshow Action After Window Loads

It makes sense to run a slideshow only when all the components for the show are present. To do that run Cycle with a .load() statement so that the slideshow waits to start until all of the images are loaded.

[crayon]
$(window).load(function() {
$(‘#big_show’).cycle({
fx: ‘fadeZoom’,
timeout: 2000,
before: onBefore
});
});
[/crayon]

If you have a big slide show with lots of images, you might want to present an animated loading image until the slideshow starts.

Calculate Image Size for Centering in Slideshow

Take a look at the solution provided by “malsup” for users of his Cycle plugin:

[crayon]
function onBefore(curr,next,opts) {
var $slide = $(next);
var w = $slide.outerWidth();
var h = $slide.outerHeight();
$slide.css({
marginTop: (400 – h) / 2,
marginLeft: (300 – w) / 2
});
};
[/crayon]

Basically, what his code is doing is creating a function that will run before any slides are manipulated by Cycle. The purpose of the onBefore function is to calculate the correct margins for images and set the CSS parameters margin-top and margin-left for centering the images.

The onBefore function creates a variable called $slide to hold an array of the parameters of the $(next) element in the slideshow. The variables w and h are set to the width and height of said element via the $slide.outerWidth() and $slide.outerHeight() methods, respectively.

Finally, the margins are calculated from knowing the width and height of the slide container as set in the CSS (in this example 400 px and 300 px) and subtracting the slide’s width (w) and height (h) values, respectively. The margins only need to have half of the total margin value to accommodate both sides of the box, so the difference is divided by 2.

Use Cycle’s Before Option to Apply Centering Function

Using the onBefore function with Cycle’s before option works beautifully with images of different sizes, see line 5 of the .load() method above. Margins are set using the .css() method after they are calculated by the onBefore function.

CSS:

[plain]
#big_show {
height: 400px;
width: 300px;
text-align: center;
background-color: #f3c;
}
.photos img {
margin: 0;
padding: 4px;
border: 1px solid #ccc;
background-color: #eee;
max-width: 290px;
max-height: 390px;
}
[/plain]

You have to specify some CSS to get this thing to work right, namely set the width and height on the slide container and slides themselves.

Setting the max-width and max-height for the images helps to keep them inside of the container. Note that adding up the padding and border for both sides of the box is 10 px of the slideshow box that can’t be used to show an image. Therefore, the maximum image dimensions are the overall width or height minus 10 px. By accounting for the padding and border sizes a large image won’t overflow its container.

Any comments on this improved slideshow using Cycle plugin with jQuery?

19 thoughts on “Slideshow Loading Problems Solved in jQuery Cycle”

  1. Hi.
    I am just starting to scratch the surface of jQuery, and Man! This is great!.
    Your explanations were great, and they address the exact issue I am having 🙂

    One question though – would setting the maxwidth and maxheight for the image resize the image proportional, resize it to fit (with distortion), or it would crop it?
    (Sorry, I don’t have way of testing this right now….)
    Thanks again for sharing your knowledge!

  2. Hey Olivier,
    Thank you for your kind words. I’m happy to have helped out.

    The values you use for the image maxwidth and maxheight are based on the size of the slideshow container, just a little smaller to allow for padding and border thickness. The purpose of specifying them is to keep the images from overflowing the container. The size of the container should be a little bigger than the largest image to keep everything in the box.

    Since the image container and image maxwidth and maxheight are manually set with some pre-knowledge of the slideshow image sizes, using maxwidth and maxheight shouldn’t resize, squish or crop the images. Make sense?

    Good luck in your jQuery ventures!

  3. Hello Axe.

    I have a slideshow (I included my website link) comprised of a row of 4 images, that cycles through 11 times (total of 44 images, I’m using divs). When my page is loaded for the first time on a mobile device or slow internet connection, you can initially see all 11 rows of 44 images on the page before everything is loaded and then the slideshow starts correctly, displays just 1 row of 4 images. I tried to take your advice above and tried your code to initially ‘hide’ the images while the page is loading and also run the slide show when all the components for the show are present (.load), but cannot get it to work. When I use the hide() code, it just hides all the images and even with the .load code, they stay hidden, never return. Is there any easier way of hiding the 44 images until the page loads, and the slideshow begins? Your help would be gratefully appreciated. I am not a javascript or jquery coder.

  4. Hi Johno,

    Might be able to suggest a solution if you could share the code that you tried for hiding the images. Without the code I could only make a guess.

  5. I had the slideshow working, but with “initial effects”. I tried the techniques here but, I clearly made some mistakes. My code is at galleryphotographica.com./test/

    1) The slideshow now sticks on the first image.

    2) I couldn’t get the images to center using the onBefore function. It might have been a syntactic error. Where in the code does the onBetween function get placed?

    3) After this is working I’d like to make the slide selection random. How will the
    $('#slides img:gt(0)').hide();
    work when the selection is random?

  6. Hey dg,

    For #1 I think there is an easy solution. Remove the last comma from your script after random:0 and it should cycle ok. So, this:
    $(‘#slides’).cycle({
    fx: ‘fade’,
    speed: 400,
    timeout: 2000,
    random: 0,
    });

    should become:
    $(‘#slides’).cycle({
    fx: ‘fade’,
    speed: 400,
    timeout: 2000,
    random: 0
    });

    For #2, place the onBefore function outside of the (document).ready statement, but in the same script. For consistency I put functions at the end of scripts and the actions that call functions at the top.

    For #3, the ‘hide all but the first slide’ statement should work as intended, but please let us know if you observe something different.
    Thanks!

  7. dg –

    Try changing this:
    $(document).load(function(){
    $(‘#slide’).cycle({

    to:
    $(window).load(function(){
    $(‘#slide’).cycle({

    and see if that gets you anywhere.

  8. You gathered everything needed in one neat article…took me a while to get all those info from many different sites before. Great work axe!

  9. I have the same exact problem as “Johno” mentioned above. In my website, almost all the links work fine except the service page which is using the same code for the slideshow but shows all 8 pictures for a fraction of a second and then back to normal I have done everything as well as following your guidelines in this but its not working! I really appreciate your help in advance.

  10. Hey Safa,

    Not sure why the images aren’t being hidden in this case, but I did see that the services page is different from the rest. You might try removing the large amazon block at the bottom of the page and see if that improves it. It was the last thing to load into the page and the other pages use smaller ads.

    If I were you, I’d probably use the following script that you have in your other pages for the services page too. Your script gives a delay of about 5 seconds before the next photo is needed for the slider and that is probably enough time for all to see it.

    Instead of this:



    just use this:

    Also, the extra tags aren’t needed and the two scripts may try to run at the same time. Try things first by removing the extra script tags. Let us know if it works better. Thanks!

    BTW – I really like how you’re using a slider in the header for this site. Good job!

  11. Pingback: jQuery cycle problem
  12. Hey Axe, I am back with good news! I finally found out where the problem was. All I needed to do was to add two important lines to the CSS describling the “slides” div where all the pictures show up in my page. I needed to make it clear that display is a “block” and the position of images in that block is “absolute”.

    here we go I put the CSS reference here:

    .slides {
    height: 250px;
    width: 898px;
    padding: 0;
    margin: 0;
    }

    .slides img {
    display: block;
    position: absolute;
    padding: 0px;
    border: 0px;
    background-color: #eee;
    height: 250px;
    width: 898px;
    }

    I hope this would help anybody solve the problem similar to mine…

  13. There’s a major flaw in this code. You write that onBefore() gets fired before the Cycle slideshow starts. This is incorrect. onBefore() is firing before every slide transition.

    This can get computationally expensive as the widths are recalculated every single time a slide changes. JQuery Cycle’s “before” and “after” options are used to define callback functions that need to operate before or after transitions, not before the slideshow as a whole begins.

    This is an important distinction if you are doing anything complex. There are many scenarios where you don’t want to be running onBefore() multiple times on a page.

  14. Hey Anthony,

    Yes, you are correct in that onBefore() is run before each slide transition. I would argue that the simple calculation of widths or heights for a slideshow isn’t that taxing on resources, but your point is well taken. Some functions, if run in multiples, could really slow a site down.

    Thanks for chiming in!

Leave a Reply

Your email address will not be published. Required fields are marked *