jQuery Plugin Creation
by benalman on Sep 13, 2009
- 17,877 views
Accessibility
Categories
Tags
More...Upload Details
Uploaded via SlideShare as Apple Keynote
Usage Rights
Statistics
- Favorites
- 11
- Downloads
- 278
- Comments
- 0
- Embed Views
- Views on SlideShare
- 4,973
- Total Views
- 17,877


* my name is ben alman
* i work at a startup just down the road, in watertown, ma called pangea media. we do a lot of cool things in the online quiz space. i’ve developed a pretty cool flash quiz widget there, created a javascript myspace application framework, co-designed a highly generalized CMS, and am now working on some twitter stuff (among other things)
* in my own time, i play the bass guitar, write and record music, i’m really into photography.. oh yeah, and i help out in the #jquery IRC channel and have written a lot of jquery plugins (feel free to check them out on my site, benalman.com, there’s something there for everyone, i think)
i’ve looked at a lot of jquery code and plugins, between the noobs in IRC and the actual legitimate plugins being authored by jquery community members.. and while i see some great code out there, i see a lot of room for improvement.
and a quick note, i might look like i’m reading a lot of this text off my screen. that’s because i AM. i’ve found that if i don’t have a script to read from, i tend to ramble. and you don’t want to be around me when i start rambling. seriously, once i get started, i’ll never stop.
first of all, style is personal. there is no right or wrong, really. - even though sometimes it feels like there is
while i don’t expect anyone to just do everything i say, i really want to get you guys thinking about developing your own style if you haven’t already. you want to do what you’re doing for a reason, hopefully a good one. and above all, be consistent.
most people use tabs. i use 2 spaces. why? well, i was forced to by a bunch of surly “code-in-emacs-in-a-shell” perl developers, but once i got used to it, i found that i really liked it.
if someone can find a way to configure firefox to display tabs at 2 characters wide, maybe i’ll reconsider. maybe.
it’s not a big deal, it’s just personal preference. but all i can say is anything that increases readability is a good thing.
also, it’s really hard to read code when there are no linebreaks separating “chunks” of code.
readability is much improved.
the people who may be using your plugin, or who might want to learn from your plugin.
many of them need all the help they can get.. so, help them.
comment them if you feel it’s helpful.
that, plus whitespace, and a few other things will all be taken care of when you minify your code.
there are a lot of other style things to keep in mind. like maybe don’t go totally overboard with ternary operators, and be sure to use curly braces around single-statement conditional blocks.
well, unless you know what you’re doing. again, it’s a matter of personal style. start simple, make sure things work, and optimize (and micro-optimize) from there. optimize last.
you need to consider your code’s API. what methods and properties do you want exposed to the end-user? if the end-user is you, and you trust yourself, which hopefully you do, maybe it doesn’t matter
but while we have two useful public methods, `setThings` and `getThings` here ...
while this is a very simple way to namespace a collection of methods (and properties), sometimes less isn't more.. for example, the uber-simplicity here results in a complete lack of privacy.
again, if you’re very trusting, you don't necessarily need privacy.. but in this example, if a user were to set simpleSingleton.things directly, they would be able to set it to a negative value, which we expressly don't want! (that’s why we have the `setThings` setter method sanitizing the input)
again, think about your code’s api. do you want users to use the `pluralize` method? probably not. and honestly, exposing it like this just gives you more to have to document.
i don’t really want to spend much time on this part of the presentation, so if you don’t understand what a closure is after my explanation, pretend like you do understand for the time being, and we can talk about it afterwards
well, every function, when invoked, creates a closure.
but let's look at another example.
because inside the onclick event handler function, i is a REFERENCE to the loop counter. and since it's just a reference to that variable, as the variable is incremented, `i` just references that changed value. after running the loop, if you were to set i = 100, they'd all alert 100.
this is no good!
since there's now an `i` inside the function, the alert sees that `i` which is ‘locked in’ at the value the function was invoked with, even though the external `i` value changes, and even though an internal function is returned.
because any functions defined inside a closure have access to all variables defined in that closure, the onclick will alert the correct index.
so, a closure allows you to create a local scope in which private variables and functions can be defined, so that they don’t need to be publicly accessible (and can’t be.. unless you’re doing some crazy eval stuff .. we won’t get into that)
since things and pluralize are now private, they aren't exposed outside the scope of the closure and are thus inaccessible to the outside. also, a side benefit here is that because everything is inside a closure, private references can be minified + obfuscated, saving space (more about that later).
here we see `unwrap`, a very simple jquery plugin i wrote (that actually looks like it will be included in 1.3.3, the patch is submitted, at least.. someone remind me to email resig about that). just be sure when writing a jQuery plugin without a closure that you use jQuery instead of $ to avoid global conflict issues, when jQuery is loaded via “noconflict”
i mean, what’s the benefit? in this instance, well.. none, other than being able to use $ throughout your code instead of jQuery. with very simple code, you might not need a closure.
i almost always use one, because I often don't know in advance when the plugin will need a closure or not, either way, less global pollution is never a bad thing.. and i like to use the $. it looks pretty.
because we want to be “smart” about things, we store all urls returned in a local cache so that if we encounter them again, we don’t have to re-fetch that url!
see the closure? it’s there.. but do we really want to use a closure here?
why? well, a closure is necessary here because an internal cache of urls is maintained. if you didn't have the closure, you’d have to either store the data in a global variable, or on jQuery somewhere, but then it might conflict with other things. unlikely, but possible.
caching things in your plugin's closure will help avoid conflicts like these.
and as i mentioned before, it will help in minification later on
but what is $.fn? it’s an alias to jQuery.prototype. like any prototype, any method you attach to that prototype ($.fn) not only appears on every jQuery object, but inside those methods, `this` refers to that object. pretty cool.
and what does $.fn.extend do? From the docs, "Extends the jQuery element set to provide new methods", just pass in an object full-o-methods, and each method in that object is attached to $.fn - just like defining those methods individually, but all in one go.
it’s not a big deal here. it's up to you how you decide to go about organizing your methods.
that being said..
i mentioned this to someone recently and they told me to "learn to code", but i beg to differ. when you define your functions individually, the commas become one less thing to worry about.. especially if you're really particular and are constantly reorganizing and refactoring code (cough).
also, defining functions separately actually allows you to save some space when minifying, using private references (more on that in a moment)
we’ve got 2 public methods. $.fn.longUrl is useful..
but should $.fn.lengthen even be public?
perhaps $.fn.lengthen isn't the most useful function to have public. You don't really want people calling it on any collection of elements, it's not going to help them. and you don’t want your `longUrl` plugin to conflict with someone else’s `lengthen` plugin if it’s not really necessary.
you could call it `longUrlLengthen` if you wanted to.. but `lengthen` is really just a convenience method for this plugin and should really be private, like the url cache
and now our plugin api is super-simple, just one method, $.fn.longUrl.
so, we're done, right?
(keep in mind these examples are simplified way down from the actual plugin, and are untested in their current incarnation)
often, with that little extra effort, you can take code originally intended for one specific use and generalize it. by generalizing, you allow your method or methods to be used in a valid, yet different way than was originally intended.
for example.. this plugin will only lengthen URLs in elements with an href attribute. which is pretty much just "a" tags. what if we wanted to lengthen a short url typed into a text field?
here, much of the code that existed in $.fn.longUrl has been broken out into a completely separate $.longUrl method. this method is useful as a general public method for "getting a long url from a short url" so it isn't kept private.
it could be private, but it's useful, so let’s keep it exposed!
more generalized method is called by $.fn.longUrl...
also: notice that the private lengthen function has been removed. it was maybe questionable that
it even existed as a separate function in the first place, since it was so simple.. but had it been more complex, of course you'd want to break it out into its own function. since the generification of the plugin simplified $.fn.longUrl, this code only existed in one place, and having a separate private function became unnecessary.
still, you don’t want to duplicate code.
if you do the same thing twice, break it out into a function. if you do very similar things in different areas, see if you can find a way to break them out into a single function that works contextually, or accepts an argument.
this probably falls under “optimization” or “don’t be a dumbass” but since it came up, i had to talk about it
well, you have slightly more documentation to write, but.. you know.
in the actual “non example” version of the plugin (that i have yet to fully generalize, actually - note the lack of a $.longUrl method) i’ve allowed the user to define an alternative callback not just for processing the `a` elements...
so now, let’s talk about..
first, a word about yui compressor. like jslint, it gives you great suggestions, but it won’t tell you how to code.. so please don’t use it as a crutch.
part of the style i've developed has come from the fact that i know that i can minify my code using
yui compressor to shrink it down in size. yui compressor will go beyond just removing unnecessary whitespace and semicolons, and actually 'munge' local, private variables that it knows don't need to maintain their names, since they can only be used inside the closure in which they are defined.
by ‘munge’ i mean reduce all instances of a long variable name to something waaaaaay shorter. as long as they are ALL reduced, it all works.
now, i don’t have time to cover every minification trick that i know, but that’s probably a good thing. a lot of these techniques get a bit over-the-top, and start to sacrifice readability for the few bytes they save, which isn’t a good thing. unless it is.. it all depends on your needs.
in general though, readability is key.
ok, that doesn’t help anyone. well, actually.. visually scanning through minified code can help you identify reused strings that are taking up a lot of space.. but still. ugleey.
you can see how all the private vars have been munged.
in this plugin, maybe it doesn’t matter... but what about when you’re creating a much larger plugin, with many more internal but public method calls?
let’s help the yui compressor make our plugin happier, without sacrificing readability too much
where a private function would have its name munged, the `longUrl` in $.longUrl won't.
now, say you had a larger plugin with a dozen internal methods that called $.longUrl many times. how can we get the file size down?
additional internal reference to this method that can be munged.
and.. just imagine the minified file size savings if you could reduce 5 or 10 (or more) `$.whatever` calls to a single variable like `a`, `b`, or `c`.
and after minification.. we saved 4 bytes! right, not really that impressive. but imagine a much larger plugin with lots of and lots of function references.
in this example, we actually replace 3 .longUrl with a string reference, saving a whopping 3 bytes. but it could be more, oh yes, it could be more.
it just depends on the size and scale of your plugin.
you can’t just “phone it in” here, you have to actually test and see if what you’re doing is making a difference, and determine if it’s worth it. make the change, minify, and see if the file is smaller. rinse and repeat.
or you can figure it out in a generic way, and take notes like i’ve done.. but who has that kind of time?
is an example taken from modernizr, which is not a jquery plugin, but something that i worked on recently with paul irish. i took the 0.9 codebase and restructured it, getting the code down, when minified, to about 60% of the previous minified size.
it was a gigantic savings.
in fact, honestly, it’s probably easier to read as the string.
sure, there’s a space savings in THIS example, but not every example will yield these benefits. a split string with any less than 6 items will cost you bytes over an array, due to the length of the method name, dot, split, parens, quotes, space, versus all the quotes and commas in the array
if you find that you're processing `constant` values by splitting strings or building regexes, you can save a huge amount of execution time by doing them in your plugin's top-level closure, and not in the individual methods, every time
plus, then every method would have access to them!
i have a real world example though..
yeah, so, originally, these two gigantic regexps (which only really need to be built once) were getting built every time linkify was being called, on every new line of chat, because i had them in the returned function.. but when i moved them into the top-level closure, they only got built just the once. this made the code run about a billion times faster. well, no, more like 60 times faster or something. it was huge.
this is what john was talking about yesterday, they just did this same thing in jquery 1.3.3 and it gave them a noticeable performance boost.
it might look gigantic, but it provides a ton of functionality in only 4kb minified.
and it can be so small because it’s very well organized internally, with lots of internal method references instead of `$.whatever` calls.
(if you don’t know what curry does, you can look it up)
note, for the sake of the example, i’ve stripped out a lot of the comments. the full code and documentation is available online on my site.
and btw, a plug, this “url utils” plugin does querystring and fragment parsing, cross-browser fragment page history, url internal / external selectors, etc..