Pre-loading images with jQuery on demand using Joel’s placeholder method

So, I think there are many ways to approach the subject of pre-loading images, or loading images on demand… but I wanted to share with you how I normally create a dynamic JavaScript based application. I call this “my logic” or “my methodology” as it’s something I thought up without looking at another developers code. I also use this same methodology in my post on the Facebook-style infinite wall posting.

I am not sure if any other people use this method, but my theory/logic uses placeholders, with a script that is called on a specific event (scrolling down/rotate/etc) to detect and load content/graphics to replace the empty placeholder. These same principals are used in my “facebook-like” infinitely scrolling wall post, just with a different application. The reason I do this, is it’s an easy way to detect how much content is not loaded, and load on demand.

In video above, I explain try to briefly explain how my placeholders work, and I’ll explain in detail below why I do things this way.

LET’S GET STARTED:

The first thing you will need is a data source, in this instance I have a JSON object holding my images and text along with as other supporting content. We’ll use this to data object build out my structure, as well as load my initial data to the page. I’ve shortened this object down to only include two items for simplicity.

var mJSON = {
    "features" : [
        {
            "title" : "Hollidaysburg",
            "droid" : "_screenshots/droid-hollidaysburg.jpg",
            "iphone" : "_screenshots/iphone-hollidaysburg.jpg",
            "web" : "_screenshots/web-hollidaysburg.jpg",
            "color" : "#E5ECF2",
            "quote" : "Extending the Schoolwires experience to the mobile environment is very exciting.  The Schoolwires mobile application looks great and is easy to use.  We look forward to offering this to our district and community.",
            "quotee" : "Robin Smith",
            "jobtitle" : "Director of Technology"
        },
        {
            "title" : "Spring Grove",
            "droid" : "_screenshots/droid-springgrove.jpg",
            "iphone" : "_screenshots/iphone-springgrove.jpg",
            "web" : "_screenshots/web-springgrove.jpg",
            "color" : "#FFFFFF",
            "quote" : "Schoolwires stands out as providing the most robust Content Management platform.  Their new Centricity2 mobile product augments this significance to our community stakeholders.",
            "quotee" : "Name",
            "jobtitle" : "Title"
        }
    ]
};

You can easily get the size of your JSON object by using the .length property. If you have never worked with JSON before, it’s worth researching and is very powerful, especially when you are building JavaScript applications. JSON is JavaScript so they work amazing when combined.

var numItems = mJSON.features.length;

The third thing you will need is your initial function to push the data to the page. You can push no data, one item, two items, etc… It’s really up to you and really should be dependent on the applications needs. In my instance I am loading two images on ready, one for the active showing image, then the image to the right we’ll be sliding to within 7 seconds.

$(document).ready(function(){
    buildData();   
});

function buildData(){
    for(j=0;j<numItems+1;j++){
        if(j==numItems) {
            $("#ss-slider").append("<img src='" + folderPath + mJSON.features[0].web + "' />");
            $("#droid-slider").append("<img src='" + folderPath + mJSON.features[0].droid + "' />");
            $("#iphone-slider").append("<img src='" + folderPath + mJSON.features[0].iphone + "' />");
        } else {
            //PUSH QUOTES
            $("#quotes-container").append("<div class='quote'><h2>" + mJSON.features[j].quote + "</h2><h5>" + mJSON.features[j].quotee + "</h5><h6>" + mJSON.features[j].jobtitle + "</h6></div>");
            //HARD INSERT FIRST TWO IMAGES
            if(j<2){
                $("#ss-slider").append("<img src='" + folderPath + mJSON.features[j].web + "' />");
                $("#droid-slider").append("<img src='" + folderPath + mJSON.features[j].droid + "' />");
                $("#iphone-slider").append("<img src='" + folderPath + mJSON.features[j].iphone + "' />");
            } else {
                //PUSH EMPTY IMAGE TAGS FOR AJAX
                $("#ss-slider").append("<span class='not-loaded' />");
                $("#droid-slider").append("<span class='not-loaded' />");
                $("#iphone-slider").append("<span class='not-loaded' />");
            }
        }
    }
    $(".quote:first").show();
    rotate();
}

You will need some sort of function to check for a change, or an event. In this case, we are sliding the images every 7 seconds. Here is my setInterval function:

function rotate(){
    window.setInterval(function(){
        slideNext();
    }, 7000);
}

The next function is the actual fun part, the animation. This is a simple jQuery based .animate() which will slide to the left the pixel width of the image area. As the animation is happening, i also call the preloader function to get the next image loaded into the page as well.

function slideNext(){
    if(active == numItems) {
        $("#ss-slider, #droid-slider, #iphone-slider").css("left","0px");
        active = 0;
    }
    $("#ss-slider").animate({
        "left" : "-=658px"
    }, 1000);
    $("#droid-slider").animate({
        "left" : "-=125px"
    }, 1000);
    $("#iphone-slider").animate({
        "left" : "-=130px"
    }, 1000);
    $(".quote").eq(active).fadeOut(600, function(){
        if($(this).next().length == 0) {
            $(".quote:first").fadeIn(1000);
        } else {
            $(this).next().fadeIn(1000);
        }
    });
    //CALL PRELOADER
    if($("#ss-slider .not-loaded").length){
        preloadImages();
    }
    active++;
}

The last function is called as the animation is happening (see above “CALL PRELOADER” comment). This function first looks into the container of slides, and gets the index() of the first “not-loaded” classed item. This is great, because it tells us which index # to pull from our data source. So we do a simple .load() function to pull in the image, and then once loaded, we place the item before the first not-loaded item, then remove the not-loaded item. Hopefully that makes sense! In this instance I have 3 images to load, one main screenshot image of the website, then two smaller images for the mobile screenshots. I load the main image, then call both the smaller images at the same time on complete of the main image load.

function preloadImages(){
    var myLoadNum = $("#ss-slider .not-loaded:first").index();
    $("<img src='" + folderPath + mJSON.features[myLoadNum].web + "'/>").load(function(){
        $(this).insertBefore($("#ss-slider .not-loaded:first"));
        $("#ss-slider .not-loaded:first").remove();
        //LOAD OTHER TWO IMAGES
        $("<img src='" + folderPath + mJSON.features[myLoadNum].droid + "'/>").load(function(){
            $(this).insertBefore($("#droid-slider .not-loaded:first"));
            $("#droid-slider .not-loaded:first").remove();
        });
        $("<img src='" + folderPath + mJSON.features[myLoadNum].iphone + "'/>").load(function(){
            $(this).insertBefore($("#iphone-slider .not-loaded:first"));
            $("#iphone-slider .not-loaded:first").remove();
        });
    });
}

Hope it all makes sense!

Joel

jQuery Facebook style “infinite” on-demand scrolling wall / feed

In this blog posting I want to show how i built a dynamic on-demand facebook style wall. Again, just like my explanation in the mobile slider posting I am using my “placeholder” methodology. I call this “my methodology” as when I set out to build this functionality, I didn’t want to just copy another developer’s creation or logic. I came up with the approach of using tags as placeholders. In this case specifically, I am using a list item structure, with all the data contained inside this structure. I place the path to the image we need to load inside this structure, then only call it when requested.

The first piece of business you’ll need to take care of is your data or database of items that need to be loaded. In this case, i am just using a simple list-item structure. I used PHP to loop through a mySQL database (wordpress DB). I push out this list-item structure based on what I need, in this case, the image, the title, permalink, and the excerpt. You can build out your list item structure, or use empty image tags with a class on them, however you’d like to build the “placeholders” really doesn’t matter.

Below is a modified version of my code, showing two already loaded list-items, and two items yet to be loaded (notice the hidden class).

<ul id="portfolio">
    <li class='portfolio-item hidden joel-clear'><a class='port-image' href='/2012/blog/?p=82'><span class='port-title'></span><span class='image-holder'>2012/03/creative.jpg</span></a>
        <div class='port-text'>
            <h2>Schoolwires Creative Services</h2>
            <p>I poured my heart and soul into this project, creating an overall design concept, then building all the front-end code, along with many custom apps, and a lot of unique animations and functionality. The site is still not available for public view, but the video I made shows the functionality and originality the site contains.</p>
            <a class='port-button' href='/2012/blog/?p=82'>VIEW PROJECT</a></div>
    </li>
    <li class='portfolio-item hidden joel-clear'><a class='port-image' href='/2012/blog/?p=7'><span class='port-title'></span><span class='image-holder'>2012/03/photogallery.jpg</span></a>
        <div class='port-text'>
            <h2>JSON Photo Gallery jQuery Plug-in</h2>
            <p>This powerful little jQuery plug-in I built has a lot of options, including navigation, link-ability, static load, random load, titles, descriptions, etc. The images are also all loaded in sequentially with AJAX calls to avoid hammering the server.</p>
            <a class='port-button' href='/2012/blog/?p=7'>VIEW PROJECT</a></div>
    </li>
    <li class='portfolio-item hidden joel-clear'><a class='port-image' href='/2012/blog/?p=4'><span class='port-title'></span><span class='image-holder'>2012/03/selfservice.jpg</span></a>
        <div class='port-text'>
            <h2>Self-Service</h2>
            <p>This tool was built in early 2011 to improve efficiencies within the Creative Services Group. We were building out pre-design templates, but we had to use source graphic files, and this made making changes not only inconsistent but also very time consuming. With this self-service tool, I removed overhead costs by requiring less staffing, significant production time reduction, and increased profit margins.</p>
            <a class='port-button' href='/2012/blog/?p=4'>VIEW PROJECT</a></div>
    </li>
    <li class='portfolio-item hidden joel-clear'><a class='port-image' href='/2012/blog/?p=10'><span class='port-title'></span><span class='image-holder'>2012/03/lakeview.jpg</span></a>
        <div class='port-text'>
            <h2>Lakeview School District</h2>
            <p>This project involved the development of a custom jQuery plug-in which will parse a list of content (Schoolwires headlines) and push the data to an array. I then take this array and turn it into an interactive animating display.</p>
            <a class='port-button' href='/2012/blog/?p=10'>VIEW PROJECT</a></div>
    </li>
</ul>

To make the initial 2 portfolio items load, i am doing a simple .each() function to loop through the first 6 items and change their class over to loading. Once I do that, I simply call my loadFolios() function, which will loop through each .loading classed item (continued below).

$(document).ready(function(){
    $(".portfolio-item.hidden").each(function(){
        if($(this).index() < 2) {
            $(this).removeClass("hidden").addClass("loading");
        }
    });
    loadFolios();
});

The loading of the actual image is contained in one simple, yet powerful function. What you see here, is when the function is called, i get the first item with the loading class then ajax in the image using the path which is contained in the .pf-thumb list-item. Once the image is loaded, I remove the loading class, and push the image into the container. When that one is complete, it looks for the next instance of a .loading class, and loads that one. Sequentially loads all the images, in order. SIMPLE, yet AMAZINGLY powerful!

function loadFolios(){
    var firstItem = $("li.portfolio-item.loading:first");
    var myImgPath = "/2012/blog/wp-content/uploads/" + $(".port-image", firstItem).text();
    $("<img />").load(function(){
        $(".image-holder", firstItem).html($(this));
        firstItem.removeClass("loading");
        //CHECKS FOR ANY NOT LOADED, IF SO, LOAD NEXT
        if($("li.portfolio-item.loading").size()){
            loadFolios();
        }
    }).attr('src',myImgPath);
}

To make the wall work when you scroll down, I built a reverse function using the built in JavaScript function scrollTop(). This basically detects “scrollBottom” which doesn’t exist. The 200 number is the range, you could set this to < 1, and only when they bottom out the scrollbar would it load more... In this case I wanted it to act more "seemless"

//THIS DETECTS SCROLL POSITION AND ADDS TWO MORE IF BELOW
$(window).scroll(function(){
    //LOAD MORE PORTFOLIOS
    var scrollBottom = $(document).height() - $(window).height() - $(window).scrollTop();
    if(scrollBottom < 200){
        $(".portfolio-item.hidden:first").removeClass("hidden").addClass("loading");
        loadFolios();
    }
});

Hope this all makes sense, and if you agree or disagree with my logic, please post up comments!

Joel

How I built jQueryJoel.com

My current site is a mix of everything I do best. The site is a mix of PHP, HTML5, responsive design (I designed this site too!), JavaScript/jQuery, and some WordPress to manage content.

The homepage is a mix of media queries and also I am using some jQuery to do some repositioning and resizing of the features rotating area. The features area is all pulled from WordPress posts, I am using PHP to connect to the WordPress database an pull out only the data I need. The same is true for the Labs area in the lower right as well. Everything is then built in HTML5 with a lot of CSS3, as well as media queries to make the responsive design work.

The sub-pages are also mainly built with my own PHP code, with the exception being the LABS area. That is all built on WordPress, and I built a theme to match my overall look and feel.

The Portfolio page is an infinitely scrolling page that i built using PHP to build out my content structure, and then some nifty jQuery I wrote to check the scrollHeight, and then AJAX in the next image dynamically.

Let me know if you have any comments, feedback, or questions on how I did anything, or if you would like clarification on any of my code.