2
\$\begingroup\$

I'm coming close to finishing a site where there are a number of jQuery functions going on. They all work, but the page is just running a bit too clunky. The fading in and out is just not as smooth as it should be, especially on the contact link, and the auto page scrolling is a little jumpy.

I've cached as many selectors as I could, I've minified the script, I'm using the latest jquery, but not much has changed. I'm wondering if someone could tell perhaps why?

$(document).ready(function ()
    {   
/*--------Variables -----------------------------------*/
    var trigger1 = $('#trigger1');
    var trigger2 = $('#trigger2');
    var trigger3 = $('#trigger3');
    var trigger4 = $('#trigger4');
    var closed = $('.close');
    var scrollContainer = $('#scrollContainer');
    var scrollOpen = $('#scrollOpen');
    var movie = $('#movie');
    var openBook = $('#openBook');
    var openChest = $('#openChest');
    var theLetter = $('#theLetter');
    var synopsis = $('#synopsis');
    var shine2 = $('#shine2');
    var bookGlow = $('#bookGlow');
    var chestGlow = $('#chestGlow');
    var iframe = $('iframe');
    var contact = $('#contact');
    var contactWrapper = $('#contactWrapper');
/*--------End Variables --------------------------------*/

/*-------------------Auto Page Scroll-------------------*/  
    $('#navigation_container').localScroll({
        duration: 1000
    });
    $('#top').localScroll({
        duration: 1000
    });
/*-----------------End Auto Page Scroll-------------------*/

/*-------------------Contact Form-------------------------*/    


        $('ul#Navlist li:last').click(function () {
             contact.delay(1000).fadeIn(3000);
             var viewportWidth = $(window).width();
             var viewportHeight = $(window).height();
             var popupWidth = contactWrapper.width();
             var popupHeight = contactWrapper.height();

             var leftPos = (viewportWidth/2)-(popupWidth/2);
             var topPos = (viewportHeight/2)-(popupHeight/2);
             contactWrapper.css('top', topPos).css('left', leftPos);
             closed.delay(1500).fadeIn(3000);
        });

        closed.click(function () {
            contact.delay(700).fadeOut('slow');
        });
    /*-------------------End Contact Form-------------------*/  


        trigger1.click(function () {
            scrollContainer.fadeIn('slow');
            scrollOpen.delay(1000).animate({
                right: 60
            }, {
                duration: 1000
            });
            closed.delay(1500).fadeIn('slow')
        });
        closed.click(function () {
            scrollOpen.animate({
                right: -579
            }, {
                duration: 1000
            });
            scrollContainer.delay(700).fadeOut('slow');
        });


        trigger2.click(function () {
            movie.delay(1000).animate({
                bottom: 0
            }, {
                duration: 1000
            });
            closed.delay(1500).fadeIn('slow')
        });
        closed.click(function () {
            iframe.attr('src', iframe.attr('src'));
            movie.delay(1000).animate({
                bottom: -500
            }, {
                duration: 1000
            });
        });


        trigger3.click(function () {
            openBook.fadeIn(1000);
            closed.delay(1000).fadeIn('slow')
        });
        closed.click(function () {
            openBook.delay(700).fadeOut('slow');
        });


        trigger4.click(function () {
            openChest.fadeIn('slow');
        });


        openChest.click(function () {
            $(this).fadeOut('slow');
            theLetter.delay(1000).fadeIn('slow');
            closed.delay(1500).fadeIn(3000);
        });
        closed.click(function () {
            theLetter.delay(700).fadeOut('slow');
        });

    /*--------------- Audio Begin ------------------------------*/
        var audio = $('#magic')[0];
        $('#trigger1, #trigger2, #trigger3, #trigger4').mouseenter(function () {
            audio.play();
        $('#synopsis,#shine2,#chestGlow,#bookGlow').fadeIn('slow');
        });
        $('#trigger1, #trigger2, #trigger3, #trigger4').mouseleave(function () {
        $('#synopsis,#shine2,#chestGlow,#bookGlow').fadeOut('slow');
        });
    /*---------------End  Audio -----------------------------*/
    })
\$\endgroup\$
  • \$\begingroup\$ I took a quick look at the page. Where do you turn off the sound? \$\endgroup\$ – Guffa May 1 '13 at 14:55
3
\$\begingroup\$

Clunky is what happens when the browser is using much of the CPU. This could be some animation, timers that keep firing, or something else. In your case:

  • You're streaming an mp3 in the background. It's a 5MB file, too big. I suggest you shrink that mp3, maybe lower its bitrate like 96kbps. It's still a good rate for background audio. The viewers are not after your audio after all.

  • The audio seems to be repetetive. Keep the audio short, around 20-30 seconds rather than almost 3 mins, and just loop it.

  • I sense you are looping the audio, but the file is downloaded every loop. The network tab of the debugger says so. Find a way that your audio file is being reused by the player from cache rather than doing a re-download. If the file isn't dynamic, you could set its cache time to a very long one.

  • Now, over to the code. True, you are caching values, and that's good. But you just don't cache any value. Cache the ones where:

    • The values get used always, especially ones that fetch DOM elements
    • Known non-dynamic values. window and document objects as well as html, head and body elements are known to be non-dynamic. Also, depending on the use case, the context object this could be used more than once especially in handlers.
  • The handlers have delayed responses in your code. I notice that triggers you have are delayed by 700ms - that's considered lag already.

    In fact, humans perceive 200ms or less as instant. If you are a gamer, you even aim for < 100ms latency at best. For interfaces, the tolerance range is around 200-400ms, . Anything greater than say 600ms is noticeable lag.

    So I suggest that when a user takes action on the UI, provide a snappy feedback. For your code, I suggest removing the delay() and fade them right away. Your fade speeds should be set to fast or within the 200-700ms range.

  • You are repetitively calling audio.play() every mouseenter. Check if the audio is already playing, and if not, that's when you call play.

  • Before running an effect, check for pending animations. If the elements are still animating, don't run another. The effects are queued and won't stop until the queue finishes.

    For example, mousing over and out of the triggers. If I mouse in and out for say 5 times, the fade in and out effects get queued. Even if I already left the section and not mousing in and out, it will continue to run until all the 5 events are finished executing.

    A handy way to prevent queuing is to add a class to the target, usually called "isAnimating". This class name is attached upon animation, and removed after animation. Before you trigger animation, you check if this exists, and if so, don't run the animation code.

  • Structure the DOM properly. Too much excess elements could use up too much memory.

  • Use the off-screen hiding technique. Instead of appending elements when they need to show up, append them early. To hide them, give them a negative left to hide them off to the left. Once needed, set them to visibility:hidden, position them where you want them to appear, and have them fade in. You are avoiding half of the DOM operations, which would be append and remove.

    However, don't forget about media elements. If you hide them off left, they will still play. Don't forget to turn them off before hiding.

  • Your site uses a lot of HD graphics and media. I suggest you do the following:

    • Preload resources that are needed immediately
    • Lazy-load the others that are needed much later. Usually, some developers create idle-loaders, which load resources when no other scripts are using the network.
    • Scripts can be loaded in parallel using script-loaders or dependency injectors. If you use RequireJS and their optimizer, all scripts are modules than can be shrunk into one script.
    • Images can be loaded using image-loading scripts to load them in parallel as well.
    • JPGs are usually smaller than PNGs but lack the alpha channel. Use JPGs for background images, and PNGs only for the elements that need to blend with the background.
    • Images can be compressed or can lose quality without visual difference. I used Adobe Fireworks before and reduced quality to 70% without noticing differences. This makes images smaller, and almost half their size.
    • Using image spriting to have images load faster by lessening HTTP requests.
  • I suggest you keep an eye on events using the debugging tools on the browser. In Chrome, ones to check are:

    • Network tab - to check the load times of the resources, which take too long or are loading more than once
    • Timeline - you can check for events, what fires when and memory usage.
    • CPU profiling - check what tasks take up too much CPU time
\$\endgroup\$
  • 1
    \$\begingroup\$ +1 for a very good answer - on my connection, Chrome reported over 100 seconds to download the MP3 (and confirms your suspicion that it was downloaded more than once). \$\endgroup\$ – RobH May 1 '13 at 12:07
  • \$\begingroup\$ Wow great informative answer. I will try all these things and get back to you. Thank you! \$\endgroup\$ – Greg May 4 '13 at 11:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.