Thinking in programming

Things I like to talk about programming

Posts Tagged ‘jquery ajax loading

“busyBox”, a simpler and cooler AJAX-Loader jQuery plugin

with 8 comments

This time I’m going to show how to create a jQuery plugin. For such purpose, I’ve thought in something simple, nice and overall, useful. With that premise in mind, lets create a plugin to make appears some HTML boxes as “busy” (useful for example, while performing an AJAX request to update some section or block of a page), thus lets name this plugin as busyBox (v1.1 released at 2010-10-12).

Yes, out there exists several plugins intended to achieved something like that. The problem I see about these plugins is they puts a transparent layer over the entire page; but which I’m wanting for, is just to put an individual transparent layer over the involved boxes in the background process. The main advantages of this technique are: the user will be able to perform other actions while waiting for others being executed, further, the user will specifically know which sections of the page are being updated without breaks his navigation workflow, and at last but not at least, the user will see a cooler effect 😉

Which I’m looking for, is to be able to use the plugin in the following way:


var box= $('my-selector');

// Show the 'busy boxes' passing some configuration
box.busyBox(
  spinner: '<img src="path-to-the-image.gif" />'
);

$.ajax(
  url: 'my-url',
  success: function(response){
    box.html(response);
  },
  complete: function(){
    // Closes the 'busy boxes'
    box.busyBox('close');
  }
);

Lets know the basic structure of a jQuery plugin:

(function($) {

  // Using this, you'll be able to use your plugin using all the jQuery's power
  $.fn.myPluginName = function() {

    // Returning "this" is needed to be able to use chaining
    return this;
  };

})(jQuery);

Now that we know the basic structure of a plugin, lets go to the action creating our own.

As first step, lets create the basic structure of our plugin:

(function($) {

 /**
  * Main function; used to initialize the plugin or for calling the available functions provides by the plugin (such as 'open' or 'close').
  * We are going to use the 'arguments' array to obtain the passed parameters
  */
  $.fn.busyBox = function() {

    if(arguments[0] == 'open') {
      // some code to put the "busy boxes" over the wanted sections of the page
    } else if(arguments[0] == 'close') {
      // some code to close the previously-opened "busy boxes"
    } else {
      // some code to initialize the plugin
    }

    // Returning "this" is needed to be able to use chaining
    return this;
  };

})(jQuery);

As second step, wold be useful to make our plugin configurable, thus, lets create some default configuration:

(function($) {

 /**
  * Main function; used to initialize the plugin or for calling the available functions provides by the plugin (such as 'open' or 'close').
  * We are going to use the 'arguments' array to obtain the passed parameters
  */
  $.fn.busyBox = function() {

    if(arguments[0] == 'open') {
      // some code to put the "busy boxes" over the wanted sections of the page
    } else if(arguments[0] == 'close') {
      // some code to close the previously-opened "busy boxes"
    } else {
      // some code to initialize the plugin
    }

    // Returning "this" is needed to be able to use chaining
    return this;
  };

 /**
  * Default configuration
  */
  $.fn.busyBox.defaults = {
    autoOpen: true,
    spinner: '<em>Loading...</em>',
    classes: 'busybox',
    top: 'auto',
    left: 'auto'
  };

})(jQuery);

As third step, lets create the basic structure of all our functions:

(function($) {
 /**
  * Main function; used to initialize the plugin or for calling the available functions provides by the plugin (such as 'open' or 'close').
  * We are going to use the 'arguments' array to obtain the passed parameters
  */
  $.fn.busyBox = function() {

    $.fn.busyBox.self = this;

    if(arguments[0] == 'open') {
      $.fn.busyBox.open();
    } else if(arguments[0] == 'close') {
      $.fn.busyBox.close();
    } else {
      $.fn.busyBox.init(arguments[0]);
    }

    return this;
  };

 /**
  * Initialize the plugin using the passed options
  */
  $.fn.busyBox.init = function(options) {

    $.fn.busyBox.opts = $.extend({}, $.fn.busyBox.defaults, options);

    // Adds the default classes if they are not present in the passed classes
    if($.fn.busyBox.opts.classes.indexOf($.fn.busyBox.defaults.classes) === -1){
      $.fn.busyBox.opts.classes += ' ' + $.fn.busyBox.defaults.classes;
    }

    $.fn.busyBox.container = $(document.body);

    if($.fn.busyBox.opts.autoOpen){
      $.fn.busyBox.open();
    }
  };

 /**
  * Display all the 'busyBoxes' over the matched boxes
  */
  $.fn.busyBox.open = function(){
    // some code to display the "busy boxes" effect
  };

 /**
  * Closes all the 'busyBoxes' being showed
  */
  $.fn.busyBox.close = function(){
    // some code to quit the "busy boxes" effect
  };

 /**
  * Default configuration
  */
  $.fn.busyBox.defaults = {
    autoOpen: true,
    spinner: '<em>Loading…</em>',
    classes: 'busybox',
    top: 'auto',
    left: 'auto'
  };

})(jQuery);

Now, putting together all the JavaScript code:

/**
 * 'busyBox' v1.1
 * @author Roger Padilla C. - rogerjose81@gmail.com
 * Web: https://rogerpadilla.wordpress.com/2010/05/24/jquery-busybox/
 * Licensed under Apached License v2.0
 */
(function($) {

	/**
	 * Main function; used to initialize the plugin or for calling the available functionalities of the plugin (such as 'open' or 'close').
	 * The 'arguments' array is used to obtain the received parameters
	 */
	$.fn.busyBox = function() {

		$.fn.busyBox.self = this;

		if(arguments[0] == 'open') {
			$.fn.busyBox.open();
		} else if(arguments[0] == 'close') {
			$.fn.busyBox.close();
		} else {
			$.fn.busyBox.init(arguments[0]);
		}

		return this;
	};

	/**
	 * Initialize the plugin using the passed options
	 */
	$.fn.busyBox.init = function(options) {

		$.fn.busyBox.opts = $.extend({}, $.fn.busyBox.defaults, options);

		// Adds the default classes if they are not present in the passed classes
		if($.fn.busyBox.opts.classes.indexOf($.fn.busyBox.defaults.classes) === -1){
			$.fn.busyBox.opts.classes += ' ' + $.fn.busyBox.defaults.classes;
		}

		$.fn.busyBox.container = $(document.body);

		if($.fn.busyBox.opts.autoOpen){
			$.fn.busyBox.open();
		}
	};

	/**
	 * Display all the 'busyBoxes' over the matched boxes
	 */
	$.fn.busyBox.open = function(){

		var box, inner, e, bOffset, bWidth, bHeight, iTop, iLeft;

		$.fn.busyBox.self.each(function(index) {

			e = $(this);
			bWidth = e.outerWidth();
			bHeight = e.outerHeight();
			bOffset = e.offset();

			box = $('<div />');
			box.attr('id', 'busybox_' + index);
			box.addClass($.fn.busyBox.opts.classes);

			box.css({
				width: bWidth,
				height: bHeight,
				top: bOffset.top,
				left: -9999 // Used to not display the box yet without hidden it (needed to be able to calculate its dimensions)
			});

			inner = $($.fn.busyBox.opts.spinner);
			inner.attr('id', 'busybox_spinner_' + index);
			inner.addClass('busybox-spinner');

			box.append(inner);

			$.fn.busyBox.container.append(box);

			// Set the position of the inner message inside its parent. Calculates the 'top' and/or 'left' coordinates of the inner-message (inside its parent) if those properties were configured as 'auto'
			iTop = ($.fn.busyBox.opts.top == 'auto' ? ((bHeight / 2) - (inner.outerHeight() / 2)) + 'px' : $.fn.busyBox.opts.top);
			iLeft = ($.fn.busyBox.opts.left == 'auto' ? ((bWidth / 2) - (inner.outerWidth() / 2)) + 'px' : $.fn.busyBox.opts.left);

			inner.css({
				position: 'absolute',
				top: iTop,
				left: iLeft,
				opacity: 1.0
			});

			// Hidde and relocate the 'busyBox' (previously displayed using a negative left-coord to be able to calculate its dimensions)
			box.css({display: 'none', left: bOffset.left});
		});

		// Display all the 'busyBoxes'
		$.fn.busyBox.container.find('.' + $.fn.busyBox.defaults.classes).fadeIn('fast', $.fn.busyBox.opts.displayed.call(this));
	};

	/**
	 * Closes all the 'busyBoxes' being showed
	 */
	$.fn.busyBox.close = function(){
		if($.fn.busyBox.container) {
			$.fn.busyBox.container.find('.' + $.fn.busyBox.defaults.classes).fadeOut('fast', function(){
				$(this).remove();
			});
		}
	};

	/**
	 * Default configuration
	 */
	$.fn.busyBox.defaults = {
		autoOpen: true,
		spinner: '<em>Cargando…</em>',
		classes: 'busybox',
		top: 'auto',
		left: 'auto',
		displayed: function(){}
	};

})(jQuery);

a bit of CSS and we are done!

.busybox {
	position: absolute;
	z-index: 999;
	color: #fff;
	background-color: #666;
	margin: 0;
	padding: 0;
	opacity: 0.5;
	filter: alpha(opacity=95);
	-ms-filter: "alpha(opacity=95)";
}

Screenshot:

Written by roger.padilla

May 24, 2010 at 10:49