How to Create Your Own jQuery Plugin

Elijah Manor | April 4th, 2010

If you have never created a jQuery plugin, it takes just a few simple steps to get started. By following a handful of guidelines, you can develop a plugin that behaves and feels like a native jQuery method.

Before I begin describing these guidelines, I want to acknowledge some sources that were instrumental when I first started learning how to write a jQuery plugin. Some of them have been around a long time, and their content is always top notch.

Why Create a jQuery Plugin?

Here is a brief list of reasons you might want to create a jQuery plugin:

  • Reuse, reuse, reuse
  • Encapsulation
  • Easy to write
  • Maintain chainability
  • Public distribution
  • Prevent namespace clashing

Among these, I think that one of the best reasons to create a plugin is to encapsulate your code for reuse across your project. Plugins are relatively easy to write, so there’s very little holding you back from cleaning up your code and making it easier to maintain. If at some point you bundle your plugin for public distribution, that is great too, but organizing your own personal code is of greatest value. If you do distribute your jQuery plugin to the public, it’s a good idea to provide a namespace for your plugin so that it doesn't collide with one of the many other plugins out there.

If none of these reasons hit home with you, know that jQuery plugins are just plain fun to write, use, and reuse.

How Does a jQuery Plugin Work?

As a somewhat simplified explanation, the basic concept of a jQuery plugin entails the following:

  • Passing a set of DOM elements from a jQuery selection
  • Manipulating the DOM elements
  • Returning the jQuery object so that you maintain chainability

Before you start addressing these three items, you first need to wire up the framework for your jQuery plugin.

Plugin Declaration

Here is the simple syntax you use to get started declaring your jQuery plugin.

 

$.fn.watermark = function(options) { /* ... */ }   
//Shortcut for jQuery.prototype.watermark = function(options) { /* ... */ }

If you were to look at the jQuery source code you would notice that $.fn is just a shortcut for jQuery.prototype. The preceding code statements are essentially the same, so using the shorter syntax is preferred.

The problem we have here is that the $ syntax could conflict with other JavaScript libraries you might be using. An easy way to get around this problem without explicitly using jQuery instead of $ is to wrap your jQuery plugin inside a self-invoking anonymous function.

What!? What is a self-invoking anonymous function? Well, essentially, it's a function without a name that is executed immediately. That way, you can call the function with jQuery as the argument and $ as the parameter, allowing you to use the $ syntax in a safe manner.

Our modified jQuery plugin will look like the following after wrapping it inside a self-invoking anonymous function:

(function($){ 
   $.fn.watermark = function(options) { ... } 
})(jQuery);

Plugin Parameters

The next step is to add some parameters to your plugin. You can do this in several ways, but the most common technique you will encounter is to pass in one parameter that has all your options inside it. If you follow this approach, the code to call your plugin will look something like the following:

$('#helloWorld').watermark({
   text: 'City', 
   class: 'watermark'
});

Instead of just doing something like watermark("City", "watermark"), this syntax is self-documenting, defining "City" as the text and "watermark" as the class.

Your plugin should also supply a set of publically accessible default plugin options in case the user doesn't pass in all of the values that you expect. This gives users a default experience, and they can also selectively override the values as they see fit.

$.fn.watermark.defaultOptions = {
   class: 'watermark',
   text: 'Enter Text Here'
}

Once you have defined your default values, you can manually override them by manipulating them outside the jQuery plugin. These changes to the default values will be implemented on all subsequent uses of the jQuery plugin.

If I want to override one of the default values used for future instances of a plugin, I would write code like the following:

$.fn.watermark.defaultOptions.class = "watermark2";
$('#helloWorld').watermark();
$('#goodbyeWorld').watermark();

This code uses the watermark2 class instead of the watermark class defined in the default options of the plugin. This overridden class is used in the two calls to the watermark plugin.

Now, that we have a section to define the default values, we need to focus on how to merge the options passed into the jQuery plugin with the default values that are defined. This is actually quite easy and can be performed using one of the jQuery utility methods. Here is an example:

(function($) {
 
   $.fn.watermark = function(options) {  
      options = $.extend({}, $.fn.watermark.defaultOptions, options);     
      return this;
   }
})(jQuery);

jQuery has an extend method that merges contents of two or more objects and stores the result in the first object. In the preceding code snippet, we merged $.fn.watermark.defaultOptions and options. The first object is an empty object because we didn't want to alter the default options for future instances of the plugin.

Looping Through a jQuery Selection

At this point our plugin isn't doing anything. But now that we have our options passed in and merged, let's focus on setting up our plugin to manipulate the DOM elements selected by jQuery.

It’s conceivable that the selector used to initiate your plugin matches zero, one, or more DOM elements. A common technique to address this possibility is to use jQuery's each method to enumerate through the elements:

this.each(function() {  
   var element = $(this);  
   //Manipulate element here ... 
}

The this keyword inside the each method represents one of the DOM elements from the selection. The code shown here wraps the DOM element in a jQuery object and stores it for future manipulation.

Public Functions

Another common technique is to publically expose one or more methods that can be called outside your plugin. To define a public method, you can use code like the following:

(function($) {  
  $.fn.watermark = function(options) {  

    options = $.extend({}, $.fn.watermark.defaultOptions, options);
    
    this.each(function() {
      var element = $(this);
      //Manipulate element here ...        
    });        
    
    return this;
  };
  
  //Public Function
  $.fn.watermark.greet = function(name) {
    console.log('Hello, ' + name + ', welcome to Script Junkies!');
  };

  $.fn.watermark.defaultOptions = {
    class: 'watermark',
    text: 'Enter Text Here'
  }
})(jQuery);

There are alternative approaches to providing public methods. A regular criticism of the technique shown just above is that it uses more than one name off the jQuery Prototype ($.fn.watermark for the plugin and $.fn.watermark.greet for the public method), and this clutters the namespace. It is  preferable to use only one name off the jQuery Prototype.

You might see some plugin authors create a module with the same name as the jQuery plugin and instantiate an object for each DOM element. The object is then stored as a data item on the DOM element for use later to call public methods.

I have rewritten the preceding code sample, which clutters the jQuery namespace ($.fn) with two public methods, and now use a Module variation that defines only one method off the jQuery namespace.You can see the modified example here.

(function($) {

   $.watermark = function(element, options) {
      this.options = {};
    
      element.data('watermark', this);
      
      this.init = function(element, options) {         
         this.options = $.extend({}, $.watermark.defaultOptions, options); 
      
         //Manipulate element here ...       
      };
      
      //Public function
      this.greet = function(name) {
         console.log('Hello, ' + name + ', welcome to Script Junkies!');
      };
      
      this.init(element, options);
   };
  
   $.fn.watermark = function(options) { //Using only one method off of $.fn  
      return this.each(function() {
         (new $.watermark($(this), options));             
      });        
   };
    
   $.watermark.defaultOptions = {
      class: 'watermark',
      text: 'Enter Text Here'
   }
})(jQuery);

Note: I use the Module code sample just shown as I continue to proceed through this tutorial. I do this mainly so that I don't clutter the public jQuery.fn namespace.

Now, calling the greet public method inside our Module isn't as nice as the previous example that cluttered the jQuery namespace. Instead, you call the public method off the $.watermark object that is stored in your DOM element using the data method. Here is an example:

//Instead of these...
$.fn.watermark.greet('Elijah'); 
$('#firstName').watermark.greet('Elijah');  

//Now use...
$('#firstName').data('watermark').greet('Elijah');

You might be thinking that isn't for you. Well, there is another frequently used technique, which is to follow the jQuery UI style of calling public methods. This technique uses the first parameter of the plugin to call public methods. The following code snippet shows how jQuery UI calls public methods off the tabs widget.

$('#tabs').tabs();
$('#tabs').tabs('add', './NewPage.html', 'New Tab');

To use this type of public method syntax, you can use the jQuery UI Widget Factory, which I’ll touch on briefly later in this article.

Private Functions

In cases for which you don't want all your functionality to be public, there are ways to make your methods private. The basic technique is to define your method  inside the self-invoking anonymous function, as shown here:

(function($) {
   $.watermark = function(element, options) {
      this.options = {};
    
      element.data('watermark', this);
      
      this.init = function(element, options) {         
          this.options = $.extend({}, $.watermark.defaultOptions, options); 

         //Call private function
         updateElement(element, this.options);
      };
            
      //Public function
      this.greet = function(name) {
         console.log('Hello, ' + name + ', welcome to Script Junkies!');
      };

      this.init(element, options);
   };
  
  $.fn.watermark = function(options) {              
    return this.each(function() {
       (new $.watermark($(this), options));           
    });        
  };
  
  //Private function
  function updateElement(element, options) {
    //Manipulate element here...   
  };

  $.watermark.defaultOptions = {
    class: 'watermark',
    text: 'Enter Text Here'
  }

})(jQuery);

Depending on the level of unit testing that you want to use you might want more of your methods to be public, which lets you test more of the methods independently. It really is up to you in the end, but the techniques I’ve illustrated should help you as you structure your code.

Support the Metadata Plugin

Currently we have defined public default values that can be overridden to affect all future jQuery plugin instances. We have also passed in custom options to the jQuery plugin that merge with the default options.

If you want your jQuery plugin to be really extensible, you can also incorporate the Metadata plugin to override options on a per-DOM-element basis. The Metadata plugin adds additional information to your DOM element by adding JSON type syntax to your element inside one of its attributes. A common technique is to use the class attribute (which is what I demonstrate), but you can also use the data attribute, as shown here:

<input id="state" type="text" class="someclass {text: 'State'}" />
<input id="zip" type="text" data="{text: 'Zip', class: 'watermark'}" />

Now let's update the jQuery plugin and add metadata support.  The following code first checks to see if the Metadata plugin exists. If it does, the code adds any metadata information from the element to the options.

this.init = function(element, options) {         
   this.options = $.extend({}, $.watermark.defaultOptions, options);

   //If Metadata plugin exists, add any metadata information from element to options
   this.options = $.metadata ? 
      $.extend({}, this.options, element.metadata()) : 
      this.options;

   updateElement(element, this.options);
};

You can now use the following syntax to override any default options or options passed to the jQuery plugin.

<input id="helloWorld" 
   type="text" 
   class="{text: 'Watermark Provided Here Instead of Plugin Argument', 
      class: 'specialWatermarkClass'}" />

Allow for Chaining

With most jQuery functions and plugins, it’s desirable to return the jQuery object to facilitate chaining. It is actually very simple to accommodate this functionality. The most common way is to return the $.each method, as you see here:

$.fn.watermark = function(options) {                
   return this.each(function() { //Maintain chainability
      (new $.watermark($(this), options));            
   });        
};

Making this minor change to your plugin enables you to call your plugin as in the following.

$('#helloWorld').watermark({text: 'Last Name'}).css({
   border-color: 'red';
});

Unit Testing

It’s a good idea to unit test your jQuery plugin to help ensure its initial quality, to be confident of your plugin's success, and to immediately identify breaking changes when you add new features or modify existing code.

I won't go into too much detail on introducing QUnit. if you want more information about how to get started, you can view the detailed documents from jQuery.com and download the appropriate files there.

To test the functionality of our watermark plugin, I put together several unit tests. For brevity's sake, I'll show only the module definition and two unit tests in this article. If you are interested in seeing or running the other tests, the source code is available on my jWatermark GitHub repository.

The following code defines the main module that I am going to use to test our watermark plugin. Because we want our unit tests to be self-contained and reproducible, I’ve defined setup and teardown methods that are called before and after each unit test so that our environment remains the same for each test.

My setup method resets the default values for the watermark plugin to the values when the plugin was first instantiated. The reason I do this is that some of my unit tests will modify the default values, and I don't want that to affect other unit tests.

I defined an empty div to exercise the watermark plugin. The teardown method basically clears the contents of the test element for use in the next unit test.

var playGroundSelector = "#qunit-playground";
module("Watermark jQuery Plugin", {
  setup: function() {
    $.watermark.defaultOptions = {
      class: 'watermark',
      text: 'Enter Text Here'
    }    
  },
  teardown: function() {
    $(playGroundSelector).empty();
  }
});

Unit tests are generally separated into three parts

  1. An Arrange section that sets up all the necessary parts for a test to occur
  2. An Act section that performs the actual test you are interested in evaluating
  3. An Assert section that validates the result of the test is what you intended

Because some of you might not be familiar with unit tests, I will annotate these sections with comments to make it easier to spot the different parts.

My first unit test is really basic. All it does is make sure that once the watermark plugin is applied with -no arguments, that the watermark text and class name match the default values provided.

test("Watermark With No Parameters", function() {
  //Arrange
  var testBox =
      $("<input id='testBox' type='text' />")
          .appendTo(playGroundSelector);
  
  //Act
  testBox.watermark();
  
  //Assert
  expect(2);
  same(testBox.attr("class"), "watermark", "Class Should Not Be Defined");
  same(testBox.val(), "Enter Text Here", "Watermark Should Not Be Default");
});

Here is another unit test that ensures that the watermark is cleared once the empty textbox has the focus.

test("Watermark Should Clear on Focus", function() {
  //Arrange
  var testBox =
      $("<input id='testBox' type='text' />")
          .appendTo(playGroundSelector);
  
  //Act
  testBox.watermark({
    text: 'Enter Text Here 4',
    class: 'watermark5'
  }).focus();
  
  //Assert
  expect(2);
  ok(!testBox.hasClass("watermark5"), "Should not have watermark Class");
  same(testBox.val(), "", "Watermark Value Should Be Cleared");
});

As  I mentioned earlier, you can write other unit tests, but I wanted to show just a couple so that you have an idea of what you can do. And again, I encourage you to write unit tests for your jQuery code. They help keep your code modular, help you maintain quality while refactoring your code, and enable you to extend your code with confidence, knowing you didn't break existing code.

jQuery UI Widgets

Once you become comfortable writing jQuery plugins you might consider looking into writing a jQuery UI widget. The main difference between the two is that a widget maintains state whereas a standard jQuery plugin typically does not.

The jQuery UI team has created a Widget Factory to make creating your widget quicker and easier. A great resource for getting up to speed on creating a jQuery UI widget is a recent blog post by Dan Wellman entitled Coding your First jQuery UI Plugin. In fact, all the existing jQuery UI widgets use the Widget Factory, so they are also a great source of examples to examine and learn from.

Conclusion

I hope this article gives you a good sense of the basic steps for how to create a reusable jQuery plugin. If you are looking for a template to jump-start your creation of a jQuery plugin, I would suggest using the Starter: Jumpstart Your jQuery Plugin template. It contains many of the concepts that I covered in this article and can provide you with a good starting point as you create your own jQuery plugin.

If you are interested in continuing your jQuery learning, you can follow me on Twitter for a fresh set of jQuery links. Also check out my blog for my daily Tech Tweets roundup of blog posts that contains numerous jQuery links to aid in your learning process.

 

About the Author

Elijah Manor is a Christian and a family man. He develops at appendTo as a Senior Architect providing corporate jQuery support, training, and consulting. He is an ASP.NET  MVP, ASPInsider, and specializes in ASP.NET MVC and jQuery development. He enjoys blogging about the things he learns. He is also active on Twitter and provides daily up-to-date Tech Tweets.

Find Elijah on: