14 Reasons Why Nobody Used Your jQuery Plugin

14 Reasons Why Nobody Used Your jQuery Plugin

Tutorial Details
  • Topic: jQuery/JavaScript
  • Difficulty: Medium
  • Estimated Completion Time: 30 Minutes

With so many folks developing jQuery plugins, it’s not uncommon to come across one that just plain – for lack of better words – sucks. There’s no examples or documentation, the plugin doesn’t follow best practices, etc. But you’re one of the lucky ones: this article will detail the pitfalls that you must avoid.

jQuery is no stranger to those of you frequent Nettuts+. Jeffrey Way’s awesome 30 Days to Learn jQuery (and various other tutorials here and elsewhere) have led us all down the path to Sizzle-powered awesomesauce. In all the hype (and a lot of leaps in JavaScript adoption by developers and browser vendors), plenty of plugins have come onto the scene. This is partially why jQuery has become the most popular JavaScript library available! The only problem is that many of them aren’t too great.

In this article, we’ll focus less on the JavaScript specifically, and more on best practices for plugin delivery.


1 – You Aren’t Making a jQuery Plugin

There are some patterns that are, more or less, universally accepted as “The Right Way” to create jQuery plugins. If you aren’t following these conventions, your plugin may… suck! Consider one of the most common patterns:

(function($, window, undefined){
$.fn.myPlugin = function(opts) {
	var defaults = {
		// setting your default values for options
	}

  // extend the options from defaults with user's options
  var options = $.extend(defaults, opts || {});

	return this.each(function(){ // jQuery chainability
	  // do plugin stuff
	});
})(jQuery, window);

First, we are creating a self-invoking anonymous function to shield ourselves from using global variables. We pass in $, window, and undefined. The arguments the self invoking function is called with are jQuery and window; nothing is passed in for undefined, so that if we decide to use the undefined keyword within the plugin, “undefined” actually will be undefined.

This shields from other scripts potentially assigning a malicious value to undefined, such as true!

$ is passed as jQuery; we do it this way to ensure that, outside of the anonymous function, $ can still refer to something else entirely, such as Prototype.

Passing the variable for the globally accessible window object allows for more compressed code through the minification processes (which you should be doing, as well).

Next, we are using the jQuery plugin pattern, $.fn.PluginName. This is a way of registering your plugin to be used with the $(selector).method() format. It simply extends jQuery’s prototype with your new method. If you want to instead create a plugin that defines a function on the jQuery object, add it directly, like so:

$.PluginName = function(options){
	// extend options, do plugin stuff
}

This type of plugin won’t be chainable, as functions that are defined as properties of the jQuery object typically don’t return the jQuery object. For instance, consider the following code:

$.splitInHalf = function(stringToSplit){
	var length = stringToSplit.length;
	var stringArray = stringToSplit.split(stringToSplit[Math.floor(length/2)]);
	return stringArray;
}

Here, we are returning an array of strings. It makes sense to simply return this as an array, as this is likely what users will want to use (and they can easily wrap it in the jQuery object if they wish). In contrast, consider the following contrived example:

$.getOddEls = function(jQcollection){ //
	return jQcollection.filter(function(index){
		var i = index+1;
		return (index % 2 != 0);
	});
}

In this case, the user is probably expecting the jQuery object to return from $.getOddEls; so, we return the filter method, which returns the jQuery collection defined by the function that is passed. A good rule of thumb is to wrap returned elements in the jQuery function, especially if they can be chained; if you are returning arrays, strings, numbers, functions, or other data types, leave them unwrapped.


2 - You Aren’t Documenting Your Code (Correctly)

Arguably, the most important thing you can do when publishing your code is add the necessary documentation. The gap between what you explain to developers and what the code actually does or can do is the time that users don’t want to waste figuring out the ins and outs of your code.

Documentation is a practice that doesn’t have any hard-fast rules; however, it is generally accepted that the more (well organized) documentation you have, the better.

This process should be both an internal practice (within/interspersed throughout your code) as well as an external practice (explaining every public method, option, and multiple use cases thoroughly in a wiki or readme).


3 – You Aren’t Providing Enough Flexibility or Customizability

The most popular plugins offer full access to variables (what most plugins refer to as “options” objects) that a user may want to control. They also may offer many different configurations of the plugin so that it is reusable in many different contexts. For instance, let’s consider a simple slider plugin. Options that the user might wish to control include the speed, type, and delay of the animation.

It’s good practice to also give the user access to classnames/ID names which are added to the DOM elements inserted or manipulated by the plugin. But beyond this, they may also want to have access to a callback function every time the slide transitions, or perhaps when the slide transitions back to the beginning (one full “cycle”).

It’s your job to think of all possible uses and needs for the plugin.

Let’s consider another example: a plugin that makes a call to an API should provide access to the API’s returned object. Take the following example of a simple plugin concep:.

$.fn.getFlickr = function(opts) {
	return this.each(function(){ // jQuery chainability
		var defaults = { // setting your default options
			cb : function(data){},
			flickrUrl : // some default value for an API call
		}
	    // extend the options from defaults with user's options
	    var options = $.extend(defaults, opts || {});

	    // call the async function and then call the callback
	    // passing in the api object that was returned
	    $.ajax(flickrUrl, function(dataReturned){
			options.cb.call(this, dataReturned);
		});
	});
}

This allows us to do something along the lines of:

	$(selector).getFlickr(function(fdata){ // flickr data is in the fdata object });

Another way of publicizing this is to offer “hooks” as options. As of jQuery 1.7.1 and up, we can use .on(eventName, function(){}) after our plugin call to separate the behaviors into their own functions. For instance, with the plugin above, we could change the code to look like this:

$.fn.getFlickr = function(opts) {
	return this.each(function(i,el){
		var $this = el;
		var defaults = { // setting your default options
			flickrUrl : "http://someurl.com" // some default value for an API call
		}
	    var options = $.extend(defaults, opts || {});

	    // call the async function and then call the callback
	    // passing in the api object that was returned
	    $.ajax(flickrUrl, function(dataReturned){
	    	// do some stuff
			$this.trigger("callback", dataReturned);
		}).error(function(){
				$this.trigger("error", dataReturned);
			});
	});
}

This allows us to call the getFlickr plugin and chain other behavior handlers.

$(selector).getFlickr(opts).on("callback", function(data){ // do stuff }).on("error", function(){ // handle an error });

You can see that offering this kind of flexibility is absolutely important; the more complex actions your plugins have, the more complex the control that should be available.


4 – You’re Requiring Too Much Configuration

Ok, so tip number three suggested that the more complex actions your plugins have, the more complex control that should be available. A big mistake, however, is making too many options required for plugin functionality. For instance, it is ideal for UI based plugins to have a no-arguments default behavior.

$(selector).myPlugin();

Certainly, sometimes this isn’t realistic (as users may be fetching a specific feed, for instance). In this case, you should do some of the heavy lifting for them. Have multiple ways of passing options to the plugin. For instance, let’s say we have a simple Tweet fetcher plugin. There should be a default behavior of that Tweet fetcher with a single required option (the username you want to fetch from).

$(selector).fetchTweets("jcutrell");

The default may, for instance, grab a single tweet, wrap it in a paragraph tag, and fill the selector element with that html. This is the kind of behavior that most developers expect and appreciate. The granular options should be just that: options.


5 – You’re Mixing External CSS Rules and Inline CSS Rules

It’s inevitable, depending upon the type of plugin, of course, that you will have to include a CSS file if it is highly based on UI manipulations. This is an acceptable solution to the problem, generally speaking; most plugins come bundled with images and CSS. But don’t forget tip number two – documentation should also include how to use/reference the stylesheet(s) and images. Developers won’t want to waste time looking through your source code to figure these things out.

Things should just…work.

With that said, it is definitely a best practice to use either injected styles (that are highly accessible via plugin options) or class/ID based styling. These IDs and classes should also be accessible, via options as previously mentioned. Inline styles override external CSS rules, however; the mixing of the two is discouraged, as it may take a developer a long time to figure out why their CSS rules aren’t being respected by elements created by your plugin. Use your best judgment in these cases.

As a rule of thumb, inline CSS is bad – unless it’s so minimal to the point that it doesn’t warrant its own external stylesheet.


6 – You Don’t Offer Examples

The proof is in the pudding: if you can’t provide a practical example of what your plugin does with accompanying code, people will quickly be turned off to using your plugin. Simple as that. Don’t be lazy.

A good template for examples:

  • A “hello world” example – usually the plugin call with the minimum configuration/options passed, and it’s accompanying html/css
  • A few more involved examples – usually with examples of full functionality of multiple options
  • An integration example – if someone might use another plugin with your plugin, here is where you can show how to do that. (This gets you bonus points in the open-source development world, too. Kudos.)

7 – Your Code Doesn’t Match Their jQuery Version

jQuery, like any good code library, grows with every release. Most methods are kept even after support is deprecated. However, new methods are added on; a perfect example of this is the .on() method, which is jQuery’s new all-in-one solution for event delegation. If you write a plugin that uses .on(), people using jQuery 1.6 or earlier will be out of luck. Now I’m not suggesting that you code for the lowest common denominator, but, in your documentation, be sure to explain which version of jQuery your plugin supports. If you introduce a plugin with support for jQuery 1.7, you should strongly consider maintaining support for 1.7 even once 1.8 comes out. You should also consider taking advantage of new/better/faster features in jQuery as they come out.

Encourage developers to upgrade, but don’t break your plugin too often! One option is to offer a “legacy” deprecated, non-supported versions of your plugin.


8 - Where’s the Changelog?

It’s time to bite the bullet if you haven’t learned how to use version control yet.

Along with keeping your jQuery version support/compatibility a part of your documentation, you should also be working in version control. Version control (specifically, via GitHub) is largely the home of social coding. If you are developing a plugin for jQuery that you want to eventually publish in the official repository, it must be stored in a GitHub repository anyway; it’s time to bite the bullet if you haven’t learned how to use version control. There are countless benefits to version control, all of which are beyond the scope of this article. But one of the core benefits is that it allows people to view the changes, improvements, and compatibility fixes you make, and when you make them. This also opens the floor for contribution and customization/extension of the plugins you write.

Additional Resources


9 – Nobody Needs Your Plugin

The world doesn’t need another slider plugin.

Ok, we’ve ignored it long enough here: some “plugins” are useless or too shallow to warrant being called a plugin. The world doesn’t need another slider plugin! It should be noted, however, that internal teams may develop their own plugins for their own uses, which is perfectly fine. However, if you’re hoping to push your plugin into the social coding sphere, find a reason to write more code. As the saying goes, there’s no reason to reinvent the wheel. Instead, take someone else’s wheel, and build a racecar. Of course, sometimes there are new and better ways of doing the same things that have already been done. For instance, you very well might write a new slider plugin if you are using faster or new technology.


10 – You Aren’t Providing a Minified Version

This one is fairly simple: offer a minified version of your code. This makes it smaller and faster. It also ensures that your Javascript is error free when compiled. When you minify your code, don’t forget to offer the uncompressed version as well, so that your peers can review the underlying code. Free and cheap tools exist for front-end developers of all levels of experience.

Refer to tip number thirteen for an automated solution.


11 – Your Code is Too Clever

When you write a plugin, it is meant to be used by others, right? For this reason, the most effective source code is highly readable. If you’re writing countless clever one-liner lambda style functions, or your variable names aren’t semantic, it will be difficult to debug errors when they inevitably occur. Instead of writing short variable names to save space, follow the advice in tip number nine (minify!). This is another part of good documentation; decent developers should be able to review your code and understand what it does without having to expend too much energy.

If you find yourself calling variables “a” or “x“, you’re doing it wrong.

Additionally, if you find yourself consulting documentation to remember what your own strange looking code is doing, you also likely need to be less concise and more explanatory. Restrict the number of lines in each function to as few as possible; if they stretch for thirty or more lines, there might be a code smell.


11.You Don’t Need jQuery

As much as we all love using jQuery, it is important to understand that it is a library, and that comes with a small cost. In general, you don’t need to worry too much about things like jQuery selector performance. Don’t be obnoxious, and you’ll be just fine. jQuery is highly optimized. That said, if the sole reason why you need jQuery (or a plugin) is to perform a few queries on the DOM, you might consider removing the abstraction entirely, and, instead, sticking with vanilla JavaScript, or Zepto.

Note: if you decide to stick with vanilla JavaScript, ensure that you’re using methods that are cross-browser. You might potentially need a small polyfill for the newer APIs.


13 – You’re Not Automating the Process

Use Grunt. Period.

Grunt is a “task-based command line build tool for JavaScript projects”, which was covered in detail recently here on Nettuts+. It allows you to do things like this:

grunt init:jquery

This line (executed in the command line) will prompt you with a set of questions, such as the title, description, version, git repository, licenses, etcetera. These pieces of information help to automate the process of setting up your documentation, licensing, etc.

Grunt does far more than just make some customized boilerplate code for you; it also offers built in tools, like the code linter JSHint, and it can automate QUnit tests for you as long as you have PhantomJS installed (which Grunt takes care of). This way, you can streamline your workflow, as tests run instantly in the terminal on save.


14 – You’re Not Testing

Oh, by the way – you do test your code, right? If not, how can you ensure/declare that your code works as expected? Manual testing has its place, but, if you find yourself refreshing the browser countless times every hour, you’re doing it wrong. Consider using tools, such as QUnit, Jasmine, or even Mocha.

Testing is particularly useful when merging in pull requests on GitHub. You can require that all requests provide tests to ensure that the new/modified code does not break your existing plugin.

If the concept of testing jQuery plugins is brand new to you, consider watching our Premium-exclusive screencast, Techniques For Test-Driving jQuery Plugins. Additionally, we’re launching a new “JavaScript Testing With Jasmine” course later this week on the site!


Some Helpful Resources

We wouldn’t be doing you any favors by just telling you what you’re doing wrong. Here are some links that will help get you back on the right path!


Closing Thoughts

If you are writing a jQuery plugin, it is vital that you stray away from the pitfalls listed above. Did I miss any key signs of a poorly executed plugin?

Tags: jQuery
Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • https://github.com/js-coder Florian

    Shouldn’t the `defaults` variable in example 1 be defined outside of `$.fn.myPlugin`? Like this `defaults` gets redefined everytime `myPlugin` is called. The default settings will be the same, so there’s no reason to redefine them again and again, right?

    • http://johnnyfreeman.us Johnny Freeman

      I was thinking the same thing! I see this all the time. The worst is when the defaults (or some other variable) is defined inside the each loop.

      I think it’s also important to note that the purpose of this article is clearly not about javascript performance. However, on the other hand, there is no arguing that the defaults should not be defined outside of the plugin. Otherwise, great article!

      If anyone is looking for a GREAT resource for writing jQuery plugins, checkout Addy Osmani’s jQuery plugin patterns.

  • http://whiteboard.is Jonathan

    Sure. Just remember that those will be defined outside of the context of the plugin itself, which is not necessarily a problem. Another way to do it is to use a .settings object property and attach a settings object.

    It’s not going to drain memory to an extent that matters, certainly, to do it this way. In my opinion, it makes it slightly more readable to keep defaults inside the $.fn.plugin call, especially since plenty of plugins call this object “defaults”.

    • http://larsjung.de Lars

      I also think that pulling the `defaults` object on step up into the iif that builds your plugin scope is a good idea. But if so you have to take care about the `$.extend` call. `$.extend` will overwrite the first argument (it also returns it, but actually overwrites it). So you need to call options = $.extend({}, defaults, opts). And I think the `opts || {}` is unnecessary, jQuery will take care of that.

      Good article!

  • http://inkwell.dotink.org Matthew J. Sahagian

    I have avoided plugins because they offer too much flexibility. I don’t see a point to loading a plugin that offers 30 ways to do something when I just need one and can do the equivalent with my own jQuery in 10 – 15 lines. Most common example of this tends to be slideshow plugins.

    • http://jonathancutrell.com Jonathan

      A lot of the times, that is the case. Plugins should be written and used for those who are wanting functionality of multiple pieces. However, if the plugin is written properly, the overhead is limited to the filesize.

      Think about jQuery for instance; a lot of people load jQuery just to use ten or or 15 of the available methods, and it’s selector engine. Sure, you could probably write more concise code yourself, but the overhead is primarily in the load size of the library, not the execution of a ton of code. This is particularly true when it comes to API wrappers – all about convenience, which is the purpose of a plugin in the first place.

      Offering options without requiring them is the trick!

    • http://johnnyfreeman.us Johnny

      Matthew – I guess I understand what you’re saying. I do the same thing for simpler things (like tabs or lightboxes). However, sometimes it’s more desirable to be able to do something the way *I* want to do it, and don’t want the plugin to dictate what my html and css looks like. This tends to require a decent amount of flexibility. But I think Jonathan hit the nail on the head when he said “Offering options without requiring them is the trick!”. This is a framework mentality. Give your users the tools to do more, but it shouldn’t be a requirement. Let them use as much or as little as they want to. And I really think more plugins devs should adopt this.

  • http://www.geertdedeckere.be/ Geert De Deckere

    Point 11 is so important and often underestimated. Well-written, clean and readable code is a must for a plugin to be maintainable. It was Joel Spolsky who said: “It’s harder to read code than to write it.”

  • 123

    Thanks for the article.

    I would disagree that no new slider plugins are needed. All the jQuery ones I tried (many) are not accessible, hence this project http://se-katc.appspot.com

    Providing *full* keyboard access for a carousel widget throws up a number of interesting complications and design decisions that do not immediately come to mind (and seem to be seldom considered).

    It’s hardly the “sexy” side of plugin development, but it’s worth the effort because people with e.g. hand tremors (i.e. unable to use a mouse) use the Web too!

  • http://www.gregfranko.com Greg Franko

    Grunt is definitely a great tool. Keep in mind that you may run into issues if you are using Jasmine with Grunt on Windows 7 (because of PhantomJS). I was able to get Grunt and Jasmine integration to work by using PhantomJS 1.3 (not a perfect workaround because it is deprecated version). You can see how I integrated Grunt by checking out my new plugin SelectBoxIt at https://github.com/gfranko/jQuery.selectBoxIt.js

  • thomas

    Reason 15: You are ignoring AMD compatibility.

  • http://bretglassett.com Bret

    Good read. I would have to say as someone who uses query plugins for projects, but doesn’t develop them, the two biggest reasons I avoid one are 1. Lack of documentation or 2. Difficult to modify (or not flexible enough). Take for example an image zoom/magnify type plugin – I would not want to write that but use them often and need to tweak per project.

  • Jesus Bejarano

    What is the diffrence between doing a plugin as a jquary plugin or doing a plugin as a class in mootools, what are the cons and pros?

    • http://whiteboard.is Jonathan Cutrell

      Well, the difference has more to do with the library than the code itself.

      MooTools is a lesser used codebase, where jQuery is easily the most popular JavaScript library currently in the wild. So writing a MooTools class may achieve the same (or similar) goals as a jQuery plugin, but with less likelihood of people being able to adopt it quickly.

      MooTools itself uses a different selector engine (which they call “Slick”), and has a slightly different feature set and approach. For instance, MooTools has a “Swiff” utility, which creates and returns a Flash object. This is something that, dare I say, will never be in jQuery core.

      • Jesus Bejarano

        Oh thanks for replay , i never think of it in that way, very insightful your respond

  • http://www.seogrep.com Kiran Chikkala

    very interesting article but i was unable to digest in the first reading.

    thanks for posting such facts at a glance.

  • Vinay

    Good Read! Well explained informative article. Thanks.

  • http://paulirish.com Paul Irish

    I actually think #10 (Provide a minified version) is a bad practice. Concatenating scripts is probably the MOST important thing you can do for a site’s performance. (followed by image compression, then gzip).

    And so shipping a minified version just invites plenty of script tags in production code. We’ve reached a point where demanding tooling for concat’ing and minifying your scripts is just your responsibility.

    • http://joakimhedlund.com Joakim Hedlund

      What about minifying AND concatenating your scripts? Just because you concatenate them doesn’t mean you shouldn’t minify them.

      The underlying problem is that the concept of keeping the amount of requests – in contrast to the size of each request – in mind is still fairly new, and needs to be spoken for more often. I for one was never taught to keep in mind that each request involves overhead data until I started reading about sprites and why they were awesome.

      I think that a majority of sites often start out as one- or two-pagers, with one CDN-jQuery and one custom JS file. I would never in my wildest dream concatenate a silly two scripts for my lil’ hobby landing page about lolcats, and it’d probably take me a while until I realize I’ve added more script tags than I should.

      In the best of worlds the developer wouldn’t have to focus on neither concatenation nor minimizing, but have the server or MVC/CMS framework do that for you. Imagine if Apache/nginx shipped with a default-enabled module for doing the heavy lifting for you, wouldn’t that be a sunny future? :-)

    • http://whiteboard.is Jonathan Cutrell

      I entirely agree that there should be increasingly more responsibility pushed to developers. And that concat’ing and minifying at deployment (with some sort of build tool) is the Right Way.

      I think that not providing a minified version would still result in about the same number of script tags, except they’d be pointing to unminified code (which is just one step more in the wrong direction).

      In the past, I would grab minified versions and drop them all into a plugins.js (H5B inspired). Isn’t this a pretty common practice? Two script tags, one of which is minified plugins/helpers/etc, and the other your site-specific stuff?

      Either way, now I personally use CodeKit to prepend my plugins.js file to my script.js file, and then compress the whole thing.

      Perhaps not offering the minified version will eventually push developers to concat then minify all together.

      • http://paulirish.com Paul Irish

        Yeah I think we’re at the right time for that last point.

        Great article btw. :)

  • Brandon

    My JS is a bit rusty (good reason to read articles here), but wouldn’t:

    $.ajax(flickrUrl, function(dataReturned){
    options.cb.call(this, dataReturned);
    });

    error since flickUrl isn’t defined? You’d need to specify the options object:

    $.ajax(options.flickrUrl, function(dataReturned){
    options.cb.call(this, dataReturned);
    });

    And when calling the code:

    $(selector).getFlickr(function(fdata){});

    I’d think you need to pass an object and not a raw function, e.g.:

    $(selector).getFlickr({cb: function(fdata){})});

    • http://www.telecommutetojuryduty.com/ Dominick

      Brandon, you look to be right on both of these accounts.

      Jonathan, you should update the code! :P

  • harinderdivine

    should i use jquery ui widget factory design pattern for building plugins or not…
    ..

    what are pros. and cons.. for this..

  • http://fabiencanu.fr Fabien

    Hello, jonathan. Is it possible to propose a French translation of this article to my readers? Citing sources of course! thank you :)

  • Kevin

    Well, I guess its time to throw my plugin out there, it violates many of these rules but I plan on moving toward utilizing GitHub here soon

    BubbleTip.js
    http://jsfiddle.net/LinuxPS2/wXHUs/

  • alibo

    So… where is reason #12 ?!

  • http://akasuna.com akasuna

    Error in your code:

    (function($, window, undefined){
    $.fn.myPlugin = function(opts) {
    var defaults = {
    // setting your default values for options
    }

    // extend the options from defaults with user’s options
    var options = $.extend(defaults, opts || {});

    return this.each(function(){ // jQuery chainability
    // do plugin stuff
    });
    }; /******** You missed this line ********/
    })(jQuery, window);

  • http://www.webdeveloperblog.co.uk Jamie Murphy

    brilliant tutorial, clearly and quickly explains every aspect that could possibly be covered with writing your own plugin for jQuery. This is by far the easiest to follow guide for this feature I have seen to date. I will definitely bookmark this page for future reference.

    Thanks again!

  • GlitchMr

    Not that I was using jQuery, but I just use void 0 instead of undefined. void keyword always uses undefined constant whatever value it is.

  • http://bappi-d-great.com Bappi D Great

    This is really a great post…

  • surya

    This is excellent and very inspired… I will rock with jquery plugin development……thank you so much