Thinking in programming

Things I like to talk about programming

“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:

Advertisements

Written by roger.padilla

May 24, 2010 at 10:49

8 Responses

Subscribe to comments with RSS.

  1. Huge fan of your plugin! it has solved many issues for me!

    THANK YOU

    Jason Gayda

    May 25, 2010 at 11:46

    • Hey Jason,

      Thank you for your feedback, you are welcome!

      roger.padilla

      May 25, 2010 at 13:06

  2. Hi Roger,
    there’s an easiest way to show loading box automatically, for every ajax query:


    $('Loading...')
    .ajaxStart(function() {$(this).show();})
    .ajaxStop(function() {$(this).hide();})
    .appendTo('body');

    Cheers!

    Eugene

    September 28, 2010 at 13:13

  3. $('<div id="busy">Loading...</div>')
    .ajaxStart(function() {$(this).show();})
    .ajaxStop(function() {$(this).hide();})
    .appendTo('body');
    

    Eugene

    September 28, 2010 at 13:14

    • Hi Eugene,

      Thanks for your comments and sorry my delay on answering!

      busyBox is not just about showing a loading-box while performing an AJAX request; with busyBox you have the ability to decide exactly which DIVs (boxes) will be showed as busy while performing an AJAX request (not just the whole container), so you can achieve, IMHO, a better looking/feedback in this way.

      Best regards everyone in booc!

      PD: You may want to check this: https://rogerpadilla.wordpress.com/2010/10/10/betterselects/

      roger.padilla

      October 10, 2010 at 21:28

  4. hi, i’m looking for a way to use this in wordpress but i’m no programmer. did you also create a wordpress plugin that uses your ajax loader?

    Thomas

    March 5, 2011 at 12:00

  5. Muchos Gracias for the article.Truly looking forward to read more.

    Priscilla Weigand

    March 17, 2012 at 03:20


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: