Creative Juices Bo. Co.

Satisfy Your Thirst For Something Refreshing!

It's Friday, How About A Few Things On jQuery

Just a Couple of Jquery Things I would like To Share

I've been working on a fairly robust website the past few weeks and I wanted to share a couple of small utility plug-ins as well as post some cool things I've discovered. The plug-ins are very rough. I'll eventually clean them up and post them in the projects section, but for now I just want to have a nice reference for them while things are fresh in my head.

Get Object Style Properties

The first utility allows you to grab and store an objects style properties. This is useful if you plan on doing an animation or some other manipulation that requires you to reset the object at a later point. You can pass a single property name like "margin-left" or "marginLeft", or a list of property names like "margin-left height border-width". It will store these values in a data property named "styles" inside the object.

The Code
/*
 Grabs and stores the object's STYLE properties.
 $('#myObject').getObjectStyle(PROPERTY NAME [, TRUE|FALSE]);
 pname = the property name to search for ("padding-left", or "paddingLeft", or "padding-left marginTop border")
 reset = use storage instead of researching for property style
 doug jones. (http://www.cjboco.com/)
*/
(function ($) {
   $.fn.getObjectStyle = function (pname, reset) {

      // a list of browser specific css style prefixes
      var vendors = ('khtml moz ms o webkit').split(' ');

      // check the reset value
	  if (typeof reset === "undefined") {
	     reset = true;
	  }

	  // converts a string to dash-case
      function dashCase(str) {
         return str.replace(/([A-Z])/gm, function (a, b, c) {
            return (c > 0 ? '-' : '') + a.toLowerCase();
         }).toLowerCase();
      }

      // converts a string to camelCase
      function camelCase(str) {
         return str.replace(/(\-[a-z])/gm, function (a, b, c) {
            return a.replace('-', '').toUpperCase();
         });
      }
      
      return this.each(function () {
         var $this = $(this),
            props = pname.split(' '),
            data = $this.data('styles'),
            dcase, ccase;

         // check obj storage and pull reference.
         if (!data) {
            $this.data('styles', {});
            data = $this.data('styles');
         }

         // loop through the provided props
         $(props).each(function (a, b) {
         
            // do our dash case fix
            dcase = dashCase(b);
            ccase = camelCase(b);
            
            // try to determine the style
            if (!reset && ccase && typeof data[ccase] === 'string') {
            
               // return data storage if stored
               return data[ccase];
               
            } else if (ccase && $this.css(ccase)) {
            
               // return normal style property
               data[ccase] = $this.css(dcase);
               return (data[ccase]);
               
            } else if (ccase) {
            
               // return any vendor properties
               $(vendors).each(function (c, d) {
                  var dash = d + '-' + dcase,
                     camel = d + ccase.charAt(0).toUpperCase() + ccase.slice(1);
                  if ($this.css(dash)) {
                     data[camel] = $this.css(dash);
                     return (data[camel]);
                  }
               });
               
            }
         });
      });
   };
}(jQuery));
Example Usage
var oldStyles;

$('#myObject').getObjectStyle('height marginBottom marginLeft', true);
oldStyle = $('#myObject').data('styles');

$('#myObject').animate({
   height: "300px",
   marginBottom: "20px",
   marginLeft: "500px"
}, 2000, function() {
   // now animate the object back to it's original settings
   $('#myObject').animate({
      height: oldStyle.height,
      marginBottom: oldStyle.marginBottom,
      marginLeft: oldStyle.marginLeft
   }, 2000);
});

The plugin is still a little rought around the edges and I'm not 100% happy with the way you retrieve the storage, but its working and that's all that's important. If you have some suggestions, let me know.

Discovered Something New in jQuery 1.5 (I think)

Another thing I've been having to deal with is performing an action after I perform a $.show() or $.hide() on a group of objects. As an example, lets say you have 200 images and you need to scale them all. I wanted to perform an action after the entire process completed. I wound up modifying the jQuery show and hide functions, here's what I had come up with:

$.showAll() and $.hideAll()
/*
 Performs and the $.show() and triggers the callback on the last item
 doug jones. (http://www.cjboco.com/)
*/
(function ($) {
   $.fn.showAll = function (speed, easing, callback) {
      var len = this.length - 1,
         opt = $.extend({}, {
            "speed": typeof speed === "number" || typeof speed === "string" ? speed : 0,
            "easing": typeof easing !== "function" ? easing : null,
            "callback": typeof easing === "function" ? easing : callback
         });
      return this.each(function (a, b) {
         // perform $.show()
         $(b).show(opt.speed, opt.easing);
         // do the callback (if exists)
         if (a === len && $.isFunction(opt.callback)) {
            // now apply the callback
            opt.callback.apply(b);
         }
      });
   };
}(jQuery));

/*
 Performs and the $.hide() and triggers the callback on the last item
 doug jones. (http://www.cjboco.com/)
*/
(function ($) {
   $.fn.hideAll = function (speed, easing, callback) {
      var len = this.length - 1,
         opt = $.extend({}, {
            "speed": typeof speed === "number" || typeof speed === "string" ? speed : 0,
            "easing": typeof easing !== "function" ? easing : null,
            "callback": typeof easing === "function" ? easing : callback
         });
      return this.each(function (a, b) {
         // perform $.show()
         $(b).hide(opt.speed, opt.easing);
         // do the callback (if exists)
         if (a === len && $.isFunction(opt.callback)) {
            // now apply the callback
            opt.callback.apply(b);
         }
      });
   };
}(jQuery));
Example Usage
<ul>
   <li class="listItem">Item 1</li>
   <li class="listItem">Item 2</li>
   <li class="listItem">Item 3</li>
   <li class="listItem">Item 4</li>
   <li class="listItem">Item 5</li>
</ul>

$('.listItem').hideAll('fast', function() {
   $('.listItem').showAll('slow', function() {
      alert('Done showing and then hiding all the objects!');
   });
});
The Problem

This solution worked, but I wasn't thrilled with it. The biggest problem with it was that if I wanted to perform an callback on each item and then trigger another callback, I couldn't. I suppose I could create another callback property to pass to the function, but I didn't want things to get to out of hand.

I started researching the jQuery docs and I came across a few leads, like using .end() at the end of the chain, but I couldn't figure out how to call a function after that. Well in the middle of my project jQuery 1.5 was released. I started reading up on it and I came across a cool new feature, Deferred callbacks.

Using A Deferred Calback

This was pretty exciting, here's what the docs has to say about the deferred calback included in the 1.5 release:

jQuery.Deferred, introduced in version 1.5, is a chainable utility object that can register multiple callbacks into callback queues, invoke callback queues, and relay the success or failure state of any synchronous or asynchronous function.

Now I can do what I wanted. I could trash my showAll and hideAll modifications and now use this instead:

$.when(
   $('.listItem').hide('fast')
).then(
   $('.listItem').show('slow')
).then(
   function() {
      alert('Done showing and then hiding all the objects!');
   }
);

How cool is that!! If they sold "I Love jQuery" t-shirts, I would buy 2. Anyway, I just thought I would take a break from my real work and share this info with you. If you have any questions or comments, scribe them down below.

EDIT: I had a little problem in the $.when().then() listing, which I have since fixed. (Needed to include a function() in the last then statement).