Creative Juices Bo. Co.

Satisfy Your Thirst For Something Refreshing!

CJ jQuery Plug-In Template 1.0

A Simple jQuery Plug-In Template Demonstrating User Callable Methods and Per Object Storage Techniques

Using jQuery had been both exciting and exhilarating from day one. Ever since my first attempt at a plug-in, I've continiually been in awe of its power and ease of use. Unfortunately, I've also have my moments of frustration trying to figure out the best way to write them. A few of my earlier attempts failed rather spectacularly. I had problems when they were used with multiple objects, weird things were happening with methods and instances of my storage variables were bleeding across objects. It was a sad state of affairs.

Since those days, I have slowly found solutions to most of the problems and have been putting what I learned into a jQuery plug-in template that I have been using for most of my newer projects. I'm sure it's not perfect and to be honest there may be things I still need to learn. But I think its at a point that I can release it onto the world and step back and see what happens.

The Template

This plug-in doesn't do much, but it does call a method, test against an object selector with an ID and ClassName and use localized storage options.

(function ($) {
   $.fn.extend({

      cjPlugInTemplate: function (opts) {

         var methods = {

         /*
          * place any "public" method functions here
          * these are typically accessible to the user
          */

            testMethod: function ($obj) {
               var data = $obj.data('cj'),
                  o = data.options;

               // ... do whatever you need to to here
               o.tempData = false;

               // example: access options by using 'o'.
               $obj.html('"testMethod" method called: tempData value is ' + o.tempData);
            }

         };

         var utilities = {

         /* 
          * you could place any private utility functions here.
          * These functions are only accessible internally.
          * Access like: utilities.testUtility($obj);
          */

            testUtility: function ($obj) {

               var data = $obj.data('cj'),
                  o = data.options;

               // ... do whatever you need to to here
               o.tempData = true;

               // example
               $obj.html('"testUtility" utility called: tempData value is ' + o.tempData);
            }

         };

         // call to the plug-in
         return this.each(function () {

            // $obj - Holds the jQuery object of our element or elements
            var $obj = $(this);

            if (typeof opts === "string" && methods[opts]) {

               /*
                * User wants to access the methods. We have
                * checked to make sure method exists. Also,
                * we are passing the object to the method. 
                * Methods don't know which object or the 
                * data storage, unless we pass this. 
                */

               if (methods[opts]) {
			      return methods[opts]($obj);
			   }

            } else if (typeof opts === "object" || !opts) {

               /*
                *   data - We store data for each element (User options, defaults, etc)
                *   o    - Quick reference to our options. You can store any use defined or default options here.
                */

               var data = $obj.data('cj'),
                  o;

               /*
                * store our options in our object. 
                * each object stores it's own "options"
                */
               if (!data) {
                  $obj.data('cj', {
                     options: {
                        tempData: null
                     }
                  });
                  data = $obj.data('cj');
               }

               // add any user defined options, if they were passed
               if (opts) {
                  data.options = $.extend(data.options, opts);
               }

               // simplify our data variables
               o = data.options;

               /* if you need to call a method you could use:
                *
                * methods['testMethod']($obj);
                * or
                * methods.testMethod($obj);
                *
                * Notice we are passing the object. Methods
                * don't know the object or the object.options
                * unless we pass this. Usefull for multiple
                * instances of the plugin.
                */

               utilities.testUtility($obj);
               
            } else {

               // unknown call to our plugin
               $.error('Method ' + opts + ' does not exist on jQuery.cjSimpleSlideShow');

            }

         });

      }
   });
}(jQuery));

Behind The Scenes

To explain inner workings of my plug-in template, you first have to understand that I'm always striving for speed. Within the past few months it seems like all my web projects have been using no less than five scripts per page. This can really bog down the system, so I'm constantly trying to trim off as much fat as possible. The basic layout out of the plug-in is this:

The Plug-In Wrapper
 -> Common Functions (Shared across all jQuery Objects)		
    -> Init and Method Call Handler
       -> Local Variables (Only Accessible to the single jQuery Object being called)

Common Functions

Common functions or methods are basically little utility scripts the perform common actions across all the jQuery objects. You might have a function that resets a counter or performs some other common task. All the plug-in templates I've come across seem to attach these to EACH jQuery object. I never thought that was efficient. I'm not sure if that's true or not, but in my eyes, this seems to waste memory since it has to copy these functions to each instance of the jQuery object. This might be ok for a single item, like <div id="myObject"></div>, but what if you have 100's of objects like <div class="myObject"></div>? In my mind, this has to be taking up memory and hence make things more sluggish (Again, not sure if this is correct or not, but it makes sense).

So, instead of declaring these common functions in the init portion of the plug-in and returning them for each object, I'm declaring them immediately towards the top and set them up to accept the jQuery object variable as an argument. This way we declare them once and be done with it.

(function ($) {
$.fn.extend({

   cjPlugInTemplate: function (opts) {

      var methods = {

         // declare all your common functions.
         // Notice that it accepts an $obj 
         // This should be the jQuery object
         // That needs to be handled...

         myFunction: function ( $obj ) {

      }

}
...

Local Variable Storage

Storing variables has been another cause of headaches. I'm not sure if it as just me or others were having the same problem, but it seemed as if I tried to store any kind of variable data, I would have problems if the plug-in was used on multiple objects. I essentially took care of this problem by making sure that each object stores it's own variables.

var $obj = $(this),
    data = $obj.data('cj'),
    o;
   
/*
 * store our options in our object. 
 * each object stores it's own "options"
 */
if (!data) {
   $obj.data('cj', {
      options: {
         tempData: null
      }
   });
   data = $obj.data('cj');
}

When we loop through each passed object, we check to see if the object has a data storage variable set named "cj". If not, we set it and store our data in there. This ensures that each object has it's own set of variables that we can do whatever we want with and not have to worry about other objects messing with the data. You could also make a variable called "isInited" to ensure that your object doesn't re-init twice.

Calling Methods

This was another one of those things I was always having problems with. And again, I'm not sure if this is the most elegant way to handle things, but it seems to be working out quite well. If you plan on creating a plug-in that allows your user to access any internal function, then you want them to be able to call them using method names. An example of this might look something like this:

$('#myObject').cjPlugInTemplate('testMethod');

Our plug-in handles this by checking the type of the data passed to the object.

// call to the plug-in
return this.each(function () {

   if (typeof opts === "string" && methods[opts]) { // <-- method[opts] ensures that the method exists
   
      /*
       * User wants to access the methods. We have
       * checked to make sure method exists. Also, 
       * we are passing the object to the method. 
       * Methods don't know which object or the 
       * data storage unless we pass this. 
       */
      
      return methods[opts]($obj);
   
   } else if (typeof opts === "object" || !opts) {
   
      // we were passed an object
   
   } else {
   
      // ??? not sure what was passed. Do error.
   
   }
}

Conclusion

I'm not really sure if I explained everything 100%, but I think I hit upon the import issues with the template. I have a simple demo and you can download the script via the sidebar up above. If you have any questions, please let me know. I'll be glad to answer them the best I can.