Writing jQuery Plugins: The Plugin Basics

At the most basic level, creating a jQuery plugin is remarkably easy. After just the first page or two of this chapter, you should have enough knowledge to create a simple plugin.

Don’t be fooled by the ease of the basic level. “Easy” to do a basic version doesn’t mean it’s easy to do it right. Following a number of best practices will ensure optimized performance, cut down on bugs, and provide the highest degree of interoperability between your code and the rest of the jQuery environment in which your code is running. This first section goes through some important plugin fundamentals.

1. Applying jQuery Plugin Naming Conventions

Imagine that after reading this chapter, you’re full of knowledge about plugin development and you’re happy to be sitting down to write your first jQuery plugin. You fire up your favorite editor or IDE, and create a new script file. Depending on your editor of choice, you may have the option of creating a filename there and then or you may not have the option until you save. Regardless of the reality in your particular editor, you’ll want to know in advance how to name your files. The answer is straightforward, but it’s nice to be prepared.

Generally, jQuery plugins are named with the following pattern:

jquery.pluginName.js

Minified versions are similarly named, adding a min flag:

jquery.pluginName.min.js

The reasoning for including jquery in the name is simple. It’s a jQuery plugin, so it’s convenient to indicate compatibility right in the filename. Some plugins come with support for multiple libraries, so this is an important touch.

Adding the .min. to minified versions allows for two versions to exist side by side, which is very handy because it enables developers to switch between full and minified versions while debugging.

2. How to Extend jQuery

Now that you know how to name your files, it’s time to actually get down to the business of extending jQuery.

jQuery offers two separate, simple patterns for extending jQuery and one more complicated, but very powerful, option to extend jQuery.

The first, and most common, is a simple alias for the prototype property of the jQuery function, jquery.fn. The second is the method jQuery.extend(). The third, more complicated option is the powerful jQuery UI Widget Factory.

2.1. Using jQuery.fn

If you’ve ever looked at the jQuery source, you’ll have seen the following very early in the source code. As of version 1.7.1, it’s visible on line 97:

jQuery.fn = jQuery.prototype = { //jquery goes here //}

Code snippet is from jquery.fn.txt

If you’re unfamiliar with the concept of prototypal inheritance, it’s the key element of JavaScript inheritance.

Simply defined, the prototype of a JavaScript object is another object that contains properties and functions native to that type of object. The core JavaScript natives all have their own prototype property. The following code example illustrates a common use of a native prototype. In this case, the example illustrates an enhancement of Array.prototype, a polyfill for missing EcmaScript functionality in nonconforming browsers. Running this code adds an [].every() method to all arrays created in the future or past on the containing page.

//from https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/every

if (!Array.prototype.every){

 Array.prototype.every = function(fun /*, thisp */){

“use strict”;

if (this === void 0 || this === null) {

throw new TypeError();

}

var t = Object(this),

len = t.length >>> 0;

if (typeof fun !== “function”){

throw new TypeError();

}

var thisp = arguments[1];

for (var i = 0; i < len; i++){

if (i in t && !fun.call(thisp, t[i], i, t)){

return false;

}

}

return true;

};

}

Code snippet is from every.txt

One interesting thing to note is that fn/prototype is live. Any and all elements created of that type are going to receive access to any prototype properties or methods added or updated at any time. JavaScript’s dot operator is specified to search the expression on the left of the operator for the key on the right, walking up the “prototype chain” until it finds a hit. Instances of objects have their internal “classes” as their prototypes, which is what makes the changes “live” for instances. This explains why you’re able to instantiate a copy of jQuery at one point and then add features and functionality to jQuery via scripts loaded later on in the page life cycle — even seconds or minutes later.

The most basic usage of jQuery.fn is as follows:

jQuery.fn.newStuff = function() {

console.log(“It’s full of (javascript) stars”);

 }; download on

                                                          Code snippet is from basic-fin-plugin.txt

This initial example is imperfect, but it’s the minimum needed to add a new function to jQuery. This pattern is enhanced and improved upon in the following pages.

2.2. Using $.extend()

In addition to JavaScript’s prototypal inheritance pattern, jQuery also offers a utility method called $.extend, which can be used to enhance jQuery. As you learned in Chapter 3, at its most basic, $.extend is a method that merges two objects. When it’s called with only a single argument, it merges the argument object into jQuery.

The following code extends jQuery with a method named newStuff:

jQuery.extend({

newStuff: function() {

console.log(“It’s full of (javascript) stars”);

}

});

Code snippet is from plugin-extend.txt

2.3. jQuery UI Widget Factory

The Widget Factory is a jQuery method that accepts two or three arguments: a namespace, an existing widget prototype to inherit from, and an optional object literal to become the new widget’s prototype.

The Widget Factory is used behind the scenes on the jQuery UI project, and it’s designed to create complex, stateful plugins with a consistent API.

The simplest possible implementation would be a single line, like this:

$.widget(‘namespace.newsStuff’, {});

  Code snippet is from simple-plugin-factory.txt

That’s a call to $.widget with a namespace (namespace.newStuff) and an empty object as the prototype. It won’t actually do much, of course, but it’s a start.

You look at more features of the Widget Factory in “A Closer Look at the Widget Factory” later in this chapter.

2.4. Which to Use?

All of these methods are valid, and you’re free to pick your preference depending on the needs of the plugin you’re writing. Choosing patterns for plugin development is discussed in more detail later in the chapter. For beginning plugin developers, either of the first two might be a better place to start.

To simplify, most of the examples that follow use the more straightforward and JavaScript-centered jQuery.fn approach.

3. General Plugin Guidelines

Now that you’ve seen the basics of extending jQuery, it’s time to look at some guidelines that will help keep your plugin development on track. From here, the basics of plugin development will, for the most part, be similar to developing jQuery applications, sites, or components. That said, you have some key differences and gotchas to keep in mind when writing plugins. This section outlines a few of those.

3.1. Remember the this Keyword

One error people commonly make when they move from basic to more advanced jQuery development is to forget where the this keyword points in the context of a plugin. In jQuery development, the this keyword commonly refers to the current DOM element being manipulated. In the immediate context of a plugin, this points to the current jQuery instance itself. So, instead of doing something like the following inside your plugin body:

$(this).toggle()

Code snippet is from extra-this.txt

you can actually just call:

this.toggle()

Code snippet is from simplified-this.txt

Inside the body of a plugin, wrapping this in $() is the equivalent of doing something like

$($(“#element”)).

The exception to that is when you’re iterating over all the elements in the current jQuery collection. Inside the body of your $.each loop, the focus of this shifts to the current DOM element that’s exposed in that particular iteration. The following code example shows this difference:

(function($){

// plugin definition

$.fn.pinkify = function() {

//this refers to jQuery, each() is immediately available

return this.each(function() {

//inside the loop this refers to the DOM element

$( this ).css({

“background” : “#fe57a1”,

“color” : “#fff”,

“text-shadow” : “none”

})

});

};

})( jQuery );

Code snippet is from remember-this.txt

3.2. Always Ensure that $ Points to jQuery

One of the most important pieces of code to use when authoring jQuery plugins is also one of the simplest. Without fail, you should use an immediately executing function expression that binds $ to jQuery. It’s always possible that some other library (commonly PrototypeJS) or other code has rewritten $ to point some other value, so it’s vital to protect against that eventuality.

The following code shows how to do this. A function expression is wrapped around the plugin definition. It accepts one argument, $, and because the function is immediately executed with jQuery as the single argument, $ is guaranteed to be bound to jQuery.

(function($) {

$.fn.newStuff = function() {

/* $ is now jQuery, no matter what value

* $ might have in the global namespace

*/

};

})( jQuery );

Code snippet is from jquery-is-jquery.txt

Additionally, there’s a common enhancement to this pattern that’s worth taking a look at. It creates local references for window and document and ensures that undefined remains undefined. This pattern provides several benefits.

Having local references for window and document allows them to be minified inside the function body (every byte counts) and speeds up access to those objects inside the function body by shortening the lookup table.

Additionally, because undefined can be reassigned in older versions of the EcmaScript standard, it’s a source of potential bugs if someone accidentally or maliciously redefines undefined. Defining it as an argument and then not defining it is a clever way to ensure that the value is as expected.

(function( $, window, document, undefined ) {

$.fn.newStuff = function() {

/* $ is now jQuery, no matter what value

* $ might have in the global namespace

*/

};

})( jQuery, window, document );

Code snippet is from window-document.-undefined.txt

3.3. Use Semicolons Properly

If you’re lucky enough to have people use your plugin, it’s important to remember that they’ll probably end up concatenating and minifying your script along with other code. To that end, it’s worth paying attention to proper semicolon usage.

If you follow good coding practice anyway, you should be fine, but it’s especially important when your code is going to live on in unknown environments.

For reference, the Google JavaScript Style Guide (http://google-styleguide.googlecode.com/ svn/trunk/javascriptguide.xml#Semicolons) says the following about semicolons:

“Relying on implicit insertion can cause subtle, hard to debug problems. Don’t do it. You’re better than that.”

Translated — semicolons are your friend.

Additionally, it’s a relatively common practice to include a leading semicolon in your plugin definition to ensure that your code will function properly in a minified and concatenated environment. It’s possible (even likely) that other code will break when minified because of poor semicolon usage or other buggy statements. Inserting a semicolon at the beginning of your plugin definition ensures that any previous statements will be explicitly ended at that point.

; (function( $, window, document, undefined ) {
   $.fn.newStuff = function() {
   };
})( jQuery, window, document );

Code snippet is from leading-semicolon.txt

3.4. Return the jQuery Object Wherever Possible

jQuery is an example of a Fluent Interface, a software design pattern identified by Martin Fowler and Eric Evans and described by Fowler in 2005 (http://www.martinfowler.com/ bliki/FluentInterface.html). The basic concept combines method chaining with an API that reads more like natural language to create a more natural, accessible API. In describing one implementation, Fowler said “The API is primarily designed to be readable and to flow.” That description could apply equally well to jQuery.

With that in mind, it’s clear that one of the key technical features of jQuery is the ability to chain methods. The concise, “Fluent” feel of the API provided by chaining was one of the features that really drew people into the library, and chaining remains a popular feature for jQuery and other JavaScript libraries, large and small.

This feature is enabled by the jQuery practice of, wherever possible, returning this, which points to the jQuery object. Because your plugin lives in that same environment, it’s vital that your plugin also return the jQuery object wherever possible. Most of the time, then, your plugins will follow the pattern outlined in the following code example. It returns this in its immediate scope and maintains the full collection so that it can be passed along to other jQuery methods.

(function( $ ) {

$.fn.pinkify = function() {

return this.css({

“background” : “#fe57a1”,

“color” : “#fff”,

“text-shadow” : “none”

});

};

})( jQuery );

Although this is a best practice and is preferred, sometimes it’s not possible to return the jQuery object. That exception is valuable to note. Whereas you generally want to allow chaining, valid reasons exist to break the chain.

Types of methods that will break the chain are those that have to return a specific value. jQuery itself offers several examples. These can be a metric, like $.height (at least when it’s called as a getter), a Boolean like $.inArray, or something else like $.type (which returns a String indicating the internal [[Class]] of a JavaScript object). The code that follows illustrates another exception. This plugin returns a Boolean indicating whether or not an element is larger than a set of provided dimensions:

(function($) {

$.fn.isWider = function( width ) {

return this.width() > width ;

};

})( jQuery );

Code snippet is from iswider.txt

3.5. Iterating Over Objects

Oftentimes with plugin development, you’re looping over all the objects in a collection and operating on each member. You will often need to do this with $.each. The following code illustrates this common pattern, returning the result of this.each:

function( $ ) {

$.fn.randomText = function() {

return this.each(function() {

$( this ).text( Math.random * 1000

})

};

})( jQuery );

Code snippet is from randomTxt.txt

Although this is common, it’s important to note that in some cases, you don’t need to use $.each. Look at the following example. The use of $.each there is redundant, because $.css will itself operate on every item in the collection.

function( $ ) {

$.fn.pinkify = function() {

return this.each(function() {

$( this ).css({

“background” : “#fe57a1”,

“color” : “#fff”,

“text-shadow” : “none”

});

});

};

})( jQuery );

Code snippet is from extra-each.txt

The preceding code can be rewritten to look like this:

function($) {

$.fn.pinkify = function() {

return this.css({

“background” : “#fe57a1”,

“color” : “#fff”,

“text-shadow” : “none”

});

};

})( jQuery );

Code snippet is from return-css.txt

Many jQuery methods will also return a full collection, so leverage that fact when authoring plugins. It’s good to leverage what jQuery offers, especially when you’re offering your code up to the community. Every little bit helps.

4. Applying Plugin Best Practices

The following section examines some plugin authoring best practices that the jQuery community has built up over the past few years. As with anything, not all of these will be applicable to every line of code you write, but it’s good to understand these concepts for your own work and to understand the work of other plugin authors even better.

4.1. Applying the jQuery Plugin Pattern

In his post “A Plugin Development Pattern” (http://www.learningjquery.com/2007/10/a- plugin-development-pattern), Mike Alsup outlines a comprehensive set of best practices to follow when developing jQuery plugins. Although the post is several years old now, it still holds up and is cited as a vital resource for plugin developers. This section examines the pattern in some detail.

Claim Only a Single Name in the jQuery Namespace

Given the large number of plugins available and the common convergence of functionality and/or plugin focus, there exists a strong possibility that plugin names will collide. To limit this as much as possible, it’s useful to claim only a single namespace for your plugin. For example, instead of offering up several related methods like this:

jQuery.fn.bestPluginEverInit = function() {

//init

};

jQuery.fn.bestPluginEverFlip = function() { {

//flip

};

jQuery.fn.bestPluginEverFlop = function() { {

//flop

};

jQuery.fn.bestPluginEverFly = function() { { //fly

};

Code snippet is from toomanynames.txt

you could, instead, create a single entry point and expose additional functionality in a more unobtrusive way.

 jQuery.fn.bestPluginEver = function() {
   //single namespace
}

Code snippet is from single-namespace.txt

From that starting point, you could use a method lookup table to expose internal methods to the public endpoint. This pattern keeps a clean namespace by encapsulating all of your methods in the plugin’s closure. They’re then called by passing the string name of the method required, and then passing any additional parameters needed for the method as a second argument. This type of method encapsulation and architecture is a standard in the jQuery plugin community and it is used by countless plugins, including the plugins and widgets in jQuery UI. The basic pattern is illustrated in the following code:

(function( $ ){

var methods = {

init : function( options ) {

// init

},

flip : function( howMany ) {

// flip

},

flop: function( ) {

// flop

},

fly : function( ) {

// fly

}

};

$.fn.bestPluginEver = function( method ) {

if ( methods[method] ) {

return methods[ method ].apply( this,

Array.prototype.slice.call( arguments, 1 ));

} else if ( typeof method === ‘object’ ||    ! method ) {

return methods.init.apply( this, arguments );

} else {

$.error( ‘Method ‘

+ method

+ ‘ does not exist in the bestPluginEver’ );

}

};

})( jQuery );

// calls the init method

$( ‘div’ ).tooltip();

// calls the fly method

$( ‘div’ ).tooltip(‘fly’);

// calls the flip method with an argument

$( ‘div’ ).tooltip(‘flip’ , ‘twice’);

Code snippet is from single-namespace-pattern.txt

Additionally, Namespace Your Events and Data

In addition to claiming just a single name in the jQuery namespace, it’s important to be frugal with your plugin events and data as well. With events and data, the issues are under the hood and are therefore less obvious. That doesn’t mean that it’s not important to play nice.

To that end, when setting events, take advantage of the ability to add event namespaces to the bound events. This simple technique enables you to safely unbind events without the danger of unbinding events bound to the same elements by code outside of the scope of your plugin.

You accomplish this by appending .namespace to the end of the bound event name. The following code illustrates this pattern by setting a namespaced resize event on the window:

(function( $ ){

var methods = { 

init : function( options )   {

$( window ).bind( ‘resize.bestFunctionEver’, methods.flop );

},

destroy : function( ) {

$( window ).unbind( ‘resize.bestFunctionEver’ );

},     flip : function( howMany ) {

// flip

},

flop: function( ) {

// flop

},

fly : function( ) {

// fly

}

};

$.fn.bestPluginEver = function( method ) {

// method calling code

};

})( jQuery );

Code snippet is from event-namespace.txt

Similarly, if your plugin needs to store data, it’s best to store it under a single $.data entry. If you need to store multiple pieces of data, you should do so with an object literal containing all of the necessary data in that single $.data entry.

The following code example shows the use of an object literal with $.data:

var data = this.data(‘bestPluginEver’); if (!data) {

download On this.data(‘bestPluginEver’,{

Wrox.com     “color” :  “blue”,

“title” : “my dashboard”,

“width” : “960px”

});

}

}

Code snippet is from namespaced-data.txt

If you follow these simple guidelines, the danger of a namespace collision is significantly reduced. This will save someone, somewhere down the road a mess of trouble.

Accept an Options Argument to Control Plugin Behavior

There’s nothing more frustrating as a developer than trying to remember the order of arguments in a function or method call — “Does the code to execute come before or after the number of milliseconds in setTimeout?” One or two are okay but anything more than that and you’re likely to have to go to the documentation to figure out what argument goes where.

Additionally, in the case where you have multiple, optional arguments, you can run into confusing function calls strewn with empty arguments, like this:

 soManyOptions(“#myDiv”, , , 14 , “#ff0000” );

Available for download on Wrox.com

No one wants to deal with code like that. It’s confusing (“Am I on the fifth argument or the fourth”) and, honestly, ugly.

To alleviate that, it’s recommended that you accept an object as an options argument that can extend the default settings when the plugin is invoked. The following code illustrates accepting an options argument and using $.extend to merge it with the plugin defaults.

This is the preferred way to provide complex configuration options.

(function( $ ){

$.fn.bestPluginEver = function( options ) {

var settings = $.extend( {

“color” : “blue”,

“title” : “my dashboard”,

“width” : “960px”

}, options);

return this.each(function() {

// best.plugin.ever });

};

})( jQuery );

$(‘div’).bestPluginEver({

‘color’ :  ‘AliceBlue’

});

Code snippet is from configuration-object.txt

Provide Public Access to Default Plugin Settings

Building on the previous example, it’s also beneficial to expose the default plugin settings directly. That way, you can set plugin defaults without having to call the plugin. This feature makes it easy for plugin users to customize the plugin across their implementation with minimal code. The following code example illustrates this. It similarly extends the default options with $.extend, but also directly exposes the $.fn.bestPluginEver.defaults object. As you’ll remember from earlier in the chapter, $.fn is an alias for jQuery.prototype, which is a live link. That means any changes to $.fn.bestPluginEver.defaults will affect any instance of the plugin created before or after the change is made to the defaults object. This is in contrast to the scope of the options argument, which affects only that specific instantiation.

One interesting thing to note is the use of an empty object passed as the first argument to $.extend. This ensures that the prototypal defaults object remains intact when the options argument is invoked. This pattern allows both for single instances to be updated with extend as well as retaining the ability to set global plugin defaults by overriding the prototypal default.

(function( $ ){

$.fn.bestPluginEver = function( options ) {

var settings = $.extend( {}, $.fn.bestPluginEver.defaults, options );

return this.each(function() {

// best.plugin.ever });

};

$.fn.bestPluginEver.defaults = {

“color” : “blue”,

“title” : “my dashboard”,

“width” : “960px”

};

})( jQuery );

//set global default by overwriting the prototypal property

$.fn.bestPluginEver.width = “768px”;

/* call the plugin with an options

* argument to override the new default in a single instance

*/

$(“div”).bestPluginEver( {“width” : “960px” });

Code snippet is from public-settings-access.txt

Provide Public Access to Secondary Functions (As Applicable)

This technique is a direct parallel to the technique outlined in the previous section. The key difference is, instead of exposing just settings to the outside world, individual methods are available to alter and extend.

In the following example, a basic sort function is defined in the plugin body. Because it’s designed to be rewriteable, it’s easy to drop in a different sorting algorithm.

(function($) {

$.fn.dataCruncher = function( options ) {

var data = $.fn.dataCruncher.sort( data );

return this.each(function() {

// do stuff with data });

};

$.fn.dataCruncher.sort = function(data) {

for ( var j = 1 test = data.length; j < test; j++ ) {

var key = data[j],

i = j – 1;

while ( i >= 0 && data[ i ] > key) {

data[ i + 1 ] = data[ i ];

i = i – 1;

}

data[i + 1] = key;

return data;

}

};

})( jQuery );

//offer a new sorting option

$.fn.dataCruncher.sort = function( data ) {

var len = data.length;

for ( var i = 0; i < len; i++ ) {

var index = i;

for ( k = i + 1; k < len; k++ ) {

if ( data[k] < data[index] ) {

index = k;

}

}

var temp = data[ i ];

data[ i ] = data[ index ];

data[ index ] = temp;

}

Code snippet is from public-methods.txt

Keep Private Functions Private

The flipside to exposing pieces of your plugin to outside enhancement is that it can cause unpredictable results if you’re not careful with identifying which pieces should be accessible and which shouldn’t.

If it’s important to the function of your plugin and won’t offer any real benefit by being exposed to plugin users, it’s probably best to keep that function private. To do that, simply define the function inside the body of your function, but outside of the return object.

(function($) {

$.fn.dataCruncher = function(options) {

//inaccessible outside the plugin function privateSort( data ){

//private method

}

return this.each(function() {

var data = privateSort( data );

});

};

Code snippet is from private-functions-private.txt

Support the Metadata Plugin

Of all of the original recommendations, this is one that isn’t as relevant as it once was. The Metadata plugin itself has been deprecated in favor of similar functionality provided by $.data(). Still, it’s worth looking at in case you find yourself working with an older version of jQuery either refactoring old code or working in an environment where the core library hasn’t been updated in some time.

The Metadata plugin extracts data from a DOM element and returns it as an object. It provides a convenient, markup-based method for customizing plugins. One example of where this can be extremely handy is if you need to extract data for use in a plugin out of a CMS. Imagine allowing CMS authors to update slideshow options by clicking a couple of checkboxes and then exposing their choices with metadata.

Support for the Metadata plugin is easy to add, so it can be a handy addition to your plugin configuration options.

The following code sample illustrates support for Metadata. It tests for the presence of $.metadata, and if the plugin is there, it merges any data found with the settings and options object that drives the style passed into CSS.

<html>

<head>

<script src=”http://code.jquery.com/jquery-1.7.1.js” ></script>

<script srcs=”jquery.metadata.js”></script>

<script>

(function($) {

// plugin definition var settings = {

“background” : “#fe57a1”,

“color” :

“text-shadow” : “none”

};

$.fn.pinkify = function( options ) {

return this.each(function() {

var style = $.extend( settings, options ),

$this = $( this );

style = $.metadata ? $.extend( style, $this.metadata() ) : style;

$this.css( style );

});

}

})( jQuery );

$( document ).ready(function() {

$(‘.pinkify’).pinkify()

});

</script>

</head>

<body>

<ul>

<li class=”pinkify”> Hot pink </li>

<li class=”pinkify { color :   ‘#000’ }”>

Black and Pink. Very hip.

</li>

<li class=”pinkify { color :   ‘green’ }”>

Not so sure about this one.

</li>

</ul>

</body>

</html>

Code snippet is from metadata.txt

Source: Otero Cesar, Rob Larsen (2012), Professional jQuery, John Wiley & Sons, Inc

Leave a Reply

Your email address will not be published. Required fields are marked *