HTML5, CSS3 & JS Experiments

By Martin Ivanov

Image Lazy Loader

Demo

Scroll the page down to gradually reveal images via lazy load. If you are interested in HTML5 Web Components you may want to check a similar example, wrapped as a custom HTML tag.

Aurellia 'That Girl' Guitar

B.C. Rich Stealth

Register the Class

<script src="AcidJs.ImgLazyload/lib/jQuery/jquery-1.10.2.min.js"></script>
<script src="AcidJs.Shortener/js/Shortener.js"></script>

Usage and Code Insight

(function() {
    "use strict";

    /*
     * Instantiate window.AcidJs.ImgLazyload
     **/
    window.imgLazyload = new window.AcidJs.ImgLazyload({
        appRoot: "pages/lazy-load/example/", // {String} [optional] path to the AcidJs.ImgLazyload folder; default: ""
        threshold: 100, // {Number} [optional] scroll threshold; default 0
        imageWidth: 480, // {Number} [optional] default: 480
        imageHeight: 360 // {Number} [optional] default: 360
    });

    /*
     * Create images list
     **/
    window.imgLazyload.loadImages({
        boundingBox: $("#aurellia-that-girl-universe"), // {jQueryDomObject} [required] HTML DOM node which will be used to render the list
        url: "pages/lazy-load/example/data/aurellia-that-girl-universe.json", // {String} [required] path to the data file
        imageWidth: 480, // {Number} [optional] default: 480
        imageHeight: 360 // {Number} [optional] default: 360
    });

    /*
     * Create images list
     **/
    window.imgLazyload.loadImages({
        boundingBox: $("#bc-rich-stealth"), // {jQueryDomObject} [required] HTML DOM node which will be used to render the list
        url: "pages/lazy-load/example/data/bc-rich-stealth.json", // {String} [required] path to the data file
        imageWidth: 480, // {Number} [optional] default: 480
        imageHeight: 360 // {Number} [optional] default: 360
    });
})();   
/*
 * AcidJs.ImgLazyload
 * JavaScript lazy loading for images
 * @namespace window.AcidJs
 * @class ImgLazyload
 * @version 2.0
 * @author Martin Ivanov
 * @url developer's website: http://wemakesites.net/
 * @url developer's twitter: https://twitter.com/#!/wemakesitesnet
 * @url developer's blog http://acidmartin.wordpress.com/
 **/

(function() {
    "use strict";

    /*
     * @namespace window.AcidJs
     **/
    if(undefined === window.AcidJs) {
        window.AcidJs = {};
    }

    /*
     * @namespace window.AcidJs
     * @class ImgLazyload
     * @constructor
     * @param config {Object}
     * {
     *  appRoot: {String} [optional] path to the AcidJs.ImgLazyload folder; default: ""
     *  threshold: {Number} [optional] scroll threshold; default 0,
     *  imageWidth: {Number} [optional] default: 480
     *  imageHeight: {Number} [optional] default: 360
     * }
     * @return {Object}
     **/
    function ImgLazyload(config) {
        
        config = config || {};
        
        for(var property in config) {
            if(config.hasOwnProperty(property)) {
                this[property] = config[property];
            }
        }
        
        this.init();
    }
    
    /*
     * @constants 
     **/
    var
        WINDOW = $(window);
    
    /*
     * @namespace window.AcidJs
     * @class ImgLazyload
     * @prototype
     * @return void
     **/
    ImgLazyload.prototype = {
        
        /*
         * Developer and application information
         * @object MANIFEST
         * @public
         **/
        MANIFEST: {
            version: "2.0",
            name: "AcidJs.ImgLazyload",
            description: "JavaScript lazy loading for images",
            developer: "Martin Ivanov",
            websites: {
                page: "http://experiments.wemakesites.net/lazy-load.html",
                personal: "http://wemakesites.net",
                portfolio: "http://acidjs.wemakesites.net",
                blog: "http://acidmartin.wordpress.com/",
                rss: "http://feeds.feedburner.com/acidmartin",
                twitter: "https://twitter.com/#!/wemakesitesnet"
            },
            email: "acid_martin@yahoo.com"
        },
        
        /*
         * HTML templates used by the class
         * @object TEMPLATES
         * @public
         **/
        TEMPLATES: {
            base: ['
  • ', '
    ', '{{src}}', '
    {{caption}}
    ', '
    ', '
  • ' ] }, /* * Scroll threshold * @number threshold * @public **/ threshold: 0, /* * Application root * @string appRoot path to the AcidJs.ImgLazyload folder * @public **/ appRoot: "", /* * Image width * @number imageWidth default image width * @public **/ imageWidth: 480, /* * Image height * @number imageWidth default image height * @public **/ imageHeight: 360, /* * URLS used by the class * @object URLS * @public **/ URLS: { baseCss: "AcidJs.ImgLazyload/styles/ImgLazyload.css" }, /* * HTML attributes used by the class * @object ATTRS * @public **/ ATTRS: { src: "data-src" }, /* * CSS classes used by the class * @object CSS_CLASSES * @public **/ CSS_CLASSES: { base: "acidjs-imglazyload", item: "acidjs-imglazyload-img" }, /* * Initialize the class * @method init * @public **/ init: function() { this.loadCss("baseCss"); this.bind(); this.lazyLoadImages(); // support server-rendered image lists automatically }, /* * Request JSON data file, containing an image list * @method loadImages * @public * @param config * { * boundingBox: {jQueryDomObject} [required] HTML DOM node which will be used to render the list * url: {String} [required] path to the data file * imageWidth: {String} [optional] default: 480 * imageHeight: {String} [optional] default: 360 * } * @return void **/ loadImages: function(config) { var that = this; $.ajax({ url: config.url, dataType: "json", success: function(data) { config.images = data.images; that.renderList(config); }, error: function() { //@todo handle error } }); }, /* * Render an image list * @method renderList * @public * @param config * { * boundingBox: {jQueryDomObject} [required] HTML DOM node which will be used to render the list * url: {String} [required] path to the data file * imageWidth: {String} [optional] default: 480 * imageHeight: {String} [optional] default: 360, * images: [{ * src: {String} path to the image * caption: {String} [optional] image caption * }] * } * @return void **/ renderList: function(config) { var html = [], that = this, boundingBox = config.boundingBox; if(!boundingBox.length) { //@todo display message return; } $.each(config.images, function(i) { var image = config.images[i]; html.push(that.compile("base", { src: image.src || "", caption: image.caption || "", appRoot: that.appRoot, imageWidth: config.imageWidth || that.imageWidth, imageHeight: config.imageHeight || that.imageHeight })); }); html = html.join(""); boundingBox.html("
      " + html + "
    ").addClass(this.CSS_CLASSES.base); this.lazyLoadImages(); }, /* * Compile a template and return the HTML * @method compile * @public * @param name {String} [required] name of the template that will be used * @param data {Object} [required] key/value pairs of template placeholders and values that should be replaced against these placeholders * @return String **/ compile: function(name, data) { var html = this.TEMPLATES[name] || []; data = data || {}; html = html.join(""); for(var key in data) { if(data.hasOwnProperty(key)) { var value = data[key], regexp = new RegExp("{{" + key + "}}", "g"); html = html.replace(regexp, value); } } return html; }, /* * Class event handlers * @method bind * @public * @return void **/ bind: function() { var that = this; WINDOW.bind("scroll", function() { that.lazyLoadImages(); }); }, /* * Lazily load images * @method lazyLoadImages * @public * @return void **/ lazyLoadImages: function() { var that = this, attrs = this.ATTRS, src = attrs.src, images = $("img[" + src + "]"); images.each(function() { var image = $(this); if(that.isImageIntoView(image)) { image.attr("src", image.attr(src)) .removeAttr(src); } }); }, /* * Check if an image is into viewport * @method isImageIntoView * @public * @param image {jQueryDomObject} * @return Boolean **/ isImageIntoView: function(image) { var threshold = this.threshold, documentViewTop = WINDOW.scrollTop() + threshold, documentViewBottom = documentViewTop + WINDOW.height(), elementTop = image.offset().top - threshold, elementBottom = elementTop + image.height(); return ((elementBottom <= documentViewBottom) && (elementTop >= documentViewTop)); }, /* * Asynchronously load a CSS file from the server * @method loadCss * @param name {String} this.URLS[name] * @private * @return void **/ loadCss: function(name) { var css = document.createElement("link"), id = this.CSS_CLASSES.base + "-" + name.toLowerCase() + "-css"; css.setAttribute("rel", "stylesheet"); css.setAttribute("href", this.appRoot + this.URLS[name]); css.setAttribute("id", id); if($("#" + id).length <= 0) { document.getElementsByTagName("head")[0].appendChild(css); } } }; /* * add to the window.AcidJs namespace **/ window.AcidJs.ImgLazyload = ImgLazyload; })();