jQuery Plugin Creation
Upcoming SlideShare
Loading in...5
×

Like this? Share it with your network

Share
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
25,892
On Slideshare
6,489
From Embeds
19,403
Number of Embeds
11

Actions

Shares
Downloads
316
Comments
0
Likes
11

Embeds 19,403

http://web.enavu.com 18,390
http://tutsvalley.com 708
http://benalman.com 151
http://translate.googleusercontent.com 68
http://www.mysrc.de 52
http://www.slideshare.net 28
http://207.46.192.232 2
http://webcache.googleusercontent.com 1
http://www.5z5.com 1
http://fanyi.youdao.com 1
http://131.253.14.250 1

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide
  • about me: <br /> * my name is ben alman <br /> * 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&#x2019;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) <br /> * in my own time, i play the bass guitar, write and record music, i&#x2019;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&#x2019;s something there for everyone, i think) <br /> <br /> i&#x2019;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. <br /> <br /> and a quick note, i might look like i&#x2019;m reading a lot of this text off my screen. that&#x2019;s because i AM. i&#x2019;ve found that if i don&#x2019;t have a script to read from, i tend to ramble. and you don&#x2019;t want to be around me when i start rambling. seriously, once i get started, i&#x2019;ll never stop.
  • so i want to start off with a few style basics. <br /> <br /> first of all, style is personal. there is no right or wrong, really. - even though sometimes it feels like there is <br /> <br /> while i don&#x2019;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&#x2019;t already. you want to do what you&#x2019;re doing for a reason, hopefully a good one. and above all, be consistent.
  • so, white space. everyone seems to have a differing opinion of how to handle white space.
  • crockford (and jslint of course) recommend 4 spaces. i see that a lot. <br /> <br /> most people use tabs. i use 2 spaces. why? well, i was forced to by a bunch of surly &#x201C;code-in-emacs-in-a-shell&#x201D; perl developers, but once i got used to it, i found that i really liked it.
  • why? well, it keeps your code much more compact, and when someone previews that code in a browser (which is almost always how they look at it for the first time, anyways) it might actually fit in there without a horizontal scrollbar. <br /> <br /> if someone can find a way to configure firefox to display tabs at 2 characters wide, maybe i&#x2019;ll reconsider. maybe. <br /> <br /> it&#x2019;s not a big deal, it&#x2019;s just personal preference. but all i can say is anything that increases readability is a good thing.
  • another thing, don&apos;t crowd your arguments! sometimes less is more, but with whitespace, more is usually more (i mean, up to a limit of course). <br /> <br /> also, it&#x2019;s really hard to read code when there are no linebreaks separating &#x201C;chunks&#x201D; of code.
  • isn&#x2019;t that better? <br /> <br /> readability is much improved.
  • and write useful comments. you don&#x2019;t have to write a book, just think about your target audience. <br /> <br /> the people who may be using your plugin, or who might want to learn from your plugin. <br /> <br /> many of them need all the help they can get.. so, help them.
  • and when you define many vars in a single &#x201C;var&#x201D; statement (like yui compressor recommends), you gain nothing by putting them all on one line. give them some breathing room. <br /> <br /> comment them if you feel it&#x2019;s helpful.
  • and don&apos;t obfuscate vars. <br /> <br /> that, plus whitespace, and a few other things will all be taken care of when you minify your code.
  • Step back and take a look. Is your code readable? cuz if it isn&#x2019;t now, it certainly won&#x2019;t be in six months. and what about when anyone else looks at it? <br /> <br /> there are a lot of other style things to keep in mind. like maybe don&#x2019;t go totally overboard with ternary operators, and be sure to use curly braces around single-statement conditional blocks. <br /> <br /> well, unless you know what you&#x2019;re doing. again, it&#x2019;s a matter of personal style. start simple, make sure things work, and optimize (and micro-optimize) from there. optimize last.
  • so i&#x2019;ll start off with some basics. most of the next few minutes shouldn&#x2019;t come as a surprise to anyone, but i&#x2019;m going to cover a few things just to make sure people understand.
  • this is a very simple singleton. a singleton is not something that you can have multiple instances of, there&#x2019;s just one of them. and i&#x2019;m only really showing this example to illustrate a point. <br /> <br /> you need to consider your code&#x2019;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&#x2019;t matter <br /> <br /> but while we have two useful public methods, `setThings` and `getThings` here ...
  • we also have a relatively useless public method `pluralize` as well as a potentially dangerous public property `things`.
  • Do we really want to expose the &#x201C;pluralize&#x201D; method? Does the user want or need this? Do we really want to expose the &#x201C;things&#x201D; property? What if the user tries to set it to -100? <br /> <br /> while this is a very simple way to namespace a collection of methods (and properties), sometimes less isn&apos;t more.. for example, the uber-simplicity here results in a complete lack of privacy. <br /> <br /> again, if you&#x2019;re very trusting, you don&apos;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&apos;t want! (that&#x2019;s why we have the `setThings` setter method sanitizing the input) <br /> <br /> again, think about your code&#x2019;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.
  • ok. many or most of you already know about closures, but i&#x2019;m going to touch on them briefly to make sure everyone is familiar with their concept and implementation <br /> <br /> i don&#x2019;t really want to spend much time on this part of the presentation, so if you don&#x2019;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
  • where might you have encountered a closure before? <br /> <br /> well, every function, when invoked, creates a closure. <br /> <br /> but let&apos;s look at another example.
  • alright. so in this example, all the links alert 10. why? <br /> <br /> because inside the onclick event handler function, i is a REFERENCE to the loop counter. and since it&apos;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&apos;d all alert 100. <br /> <br /> this is no good!
  • what&apos;s the fix? create a closure!
  • hallelujah! the wrapper function expression is invoked immediately, and the current value of `i` is passed into the function as an argument, to be used internally. <br /> <br /> since there&apos;s now an `i` inside the function, the alert sees that `i` which is &#x2018;locked in&#x2019; at the value the function was invoked with, even though the external `i` value changes, and even though an internal function is returned. <br /> <br /> because any functions defined inside a closure have access to all variables defined in that closure, the onclick will alert the correct index.
  • and an alternative take on the closure is to just wrap the entire onclick definition in a function, and invoke it immediately. this basically does the same thing as the previous example, but in my opinion is a little bit less confusing looking. `i` is still locked in, but in a slightly different way. <br /> <br /> so, a closure allows you to create a local scope in which private variables and functions can be defined, so that they don&#x2019;t need to be publicly accessible (and can&#x2019;t be.. unless you&#x2019;re doing some crazy eval stuff .. we won&#x2019;t get into that)
  • ok, back to the singleton.
  • so, let&#x2019;s solve our little dilemma by using a closure
  • alright. so, here&apos;s the basic singleton, but this time using a closure for privacy. the surrounding function expression is invoked immediately, creating a closure, and returns an object reference that contains 2 methods. just like in the previous closure examples, things and pluralize are private and available to all other functions that are defined inside the closure, namely the `setThings` and `getThings` methods. <br /> <br /> since things and pluralize are now private, they aren&apos;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).
  • so onto some more interesting stuff (maybe) <br /> <br /> 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 &#x201C;noconflict&#x201D;
  • so, do we need a closure here?
  • maybe. whatever. <br /> <br /> i mean, what&#x2019;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. <br /> <br /> i almost always use one, because I often don&apos;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.
  • here&apos;s a slightly more complicated example. let&#x2019;s say that we have this longUrl plugin. you call it on some number of &#x2018;a&#x2019; elements, and it iterates over them, passing any short urls to an external service that returns the long url that short url redirects to (which it then sets as that element&#x2019;s title attribute). <br /> <br /> because we want to be &#x201C;smart&#x201D; about things, we store all urls returned in a local cache so that if we encounter them again, we don&#x2019;t have to re-fetch that url! <br /> <br /> see the closure? it&#x2019;s there.. but do we really want to use a closure here?
  • the answer is yes! <br /> <br /> why? well, a closure is necessary here because an internal cache of urls is maintained. if you didn&apos;t have the closure, you&#x2019;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. <br /> <br /> caching things in your plugin&apos;s closure will help avoid conflicts like these. <br /> <br /> and as i mentioned before, it will help in minification later on
  • now, we&#x2019;re working with a very similar organization to our singleton-in-a-closure from before. <br /> <br /> but what is $.fn? it&#x2019;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. <br /> <br /> 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.
  • or you could do it this way, with individual method definitions instead of $.fn.extend. <br /> <br /> it&#x2019;s not a big deal here. it&apos;s up to you how you decide to go about organizing your methods. <br /> <br /> that being said..
  • one of the reasons i prefer individual function definitions on $.fn vs using $.fn.extend with an object is because it results in one less level of indentation, which i find lends itself to more readable code.
  • another thing is those pesky trailing commas that ie loves so much. <br /> <br /> 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&apos;re really particular and are constantly reorganizing and refactoring code (cough). <br /> <br /> also, defining functions separately actually allows you to save some space when minifying, using private references (more on that in a moment)
  • ok, so let&#x2019;s think about the plugin api we&#x2019;ve created for a second. <br /> <br /> we&#x2019;ve got 2 public methods. $.fn.longUrl is useful.. <br /> <br /> but should $.fn.lengthen even be public?
  • probably not. <br /> <br /> perhaps $.fn.lengthen isn&apos;t the most useful function to have public. You don&apos;t really want people calling it on any collection of elements, it&apos;s not going to help them. and you don&#x2019;t want your `longUrl` plugin to conflict with someone else&#x2019;s `lengthen` plugin if it&#x2019;s not really necessary. <br /> <br /> 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
  • so, ok. just like the cache var, lengthen is now defined as private. <br /> <br /> and now our plugin api is super-simple, just one method, $.fn.longUrl.
  • so, we now have a working plugin <br /> <br /> so, we&apos;re done, right? <br /> <br /> (keep in mind these examples are simplified way down from the actual plugin, and are untested in their current incarnation)
  • well, we could be done. but why not put in just a little extra effort and make it even better? <br /> <br /> 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. <br /> <br /> 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?
  • so yeah, let&apos;s generalize a little.
  • alright. how can we generalize this? well, why not make a more generic $.longUrl function that will operate on any short url string passed in and return the long url (via callback, of course, because the $.getJSON request it uses is asynchronous)
  • so, take a look at this refactored code. <br /> <br /> 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&apos;t kept private. <br /> <br /> it could be private, but it&apos;s useful, so let&#x2019;s keep it exposed!
  • now, this new generic $.longUrl method can be used anytime, anywhere, by anyone. and since this <br /> more generalized method is called by $.fn.longUrl...
  • ...that method can be made much simpler, reducing any possible redundant code. and it&apos;s not just our internal $.fn.longUrl method that can benefit from this - any other code can utilize $.longUrl now. <br /> <br /> also: notice that the private lengthen function has been removed. it was maybe questionable that <br /> 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&apos;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. <br /> <br /> still, you don&#x2019;t want to duplicate code.
  • no, really. i mean it. <br /> <br /> 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. <br /> <br /> this probably falls under &#x201C;optimization&#x201D; or &#x201C;don&#x2019;t be a dumbass&#x201D; but since it came up, i had to talk about it
  • ok, so we&#x2019;ve got a generalized plugin. how does it feel? we&#x2019;ve gone above and beyond the original &#x201C;spec&#x201D; for the plugin in a way that nobody can complain about, and we haven&#x2019;t even really made it more complicated. <br /> <br /> well, you have slightly more documentation to write, but.. you know.
  • and there&#x2019;s a lot of ways to generalize your code. <br /> <br /> in the actual &#x201C;non example&#x201D; version of the plugin (that i have yet to fully generalize, actually - note the lack of a $.longUrl method) i&#x2019;ve allowed the user to define an alternative callback not just for processing the `a` elements...
  • but also an alternative callback for making the api call...
  • as well as a few other parameters.
  • and i&#x2019;m using $.extend inside the function...
  • to allow the user to override the default options with their own options (this is a very common way to handle options, we see it a lot) <br /> <br /> so now, let&#x2019;s talk about..
  • minification, finally. <br /> <br /> first, a word about yui compressor. like jslint, it gives you great suggestions, but it won&#x2019;t tell you how to code.. so please don&#x2019;t use it as a crutch. <br /> <br /> part of the style i&apos;ve developed has come from the fact that i know that i can minify my code using <br /> yui compressor to shrink it down in size. yui compressor will go beyond just removing unnecessary whitespace and semicolons, and actually &apos;munge&apos; local, private variables that it knows don&apos;t need to maintain their names, since they can only be used inside the closure in which they are defined. <br /> <br /> by &#x2018;munge&#x2019; i mean reduce all instances of a long variable name to something waaaaaay shorter. as long as they are ALL reduced, it all works. <br /> <br /> now, i don&#x2019;t have time to cover every minification trick that i know, but that&#x2019;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&#x2019;t a good thing. unless it is.. it all depends on your needs. <br /> <br /> in general though, readability is key.
  • so, here&#x2019;s our code, unminified
  • aaaand.. this is what our code looks like when minified <br /> <br /> ok, that doesn&#x2019;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.
  • ok. slightly less unreadable <br /> <br /> you can see how all the private vars have been munged.
  • but what about that $.longUrl call in the $.fn.longUrl method? <br /> <br /> in this plugin, maybe it doesn&#x2019;t matter... but what about when you&#x2019;re creating a much larger plugin, with many more internal but public method calls? <br /> <br /> let&#x2019;s help the yui compressor make our plugin happier, without sacrificing readability too much
  • because object properties and methods can be accessed via dot syntax or square-brackets-with-a-string syntax, it not possible for a minifier to actually munge them (like simple vars). so, if you have a lot of long object properties referenced in your code, you might end up with a lot of wasted bytes.
  • in this example, $.longUrl is called internally just the once. <br /> <br /> where a private function would have its name munged, the `longUrl` in $.longUrl won&apos;t. <br /> <br /> 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?
  • ok, here&#x2019;s one way..
  • when you look at the code, you see that $.longUrl is still public, but now, there&apos;s an <br /> additional internal reference to this method that can be munged. <br /> <br /> 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`. <br /> <br /> 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.
  • and another approach...
  • here, we&#x2019;ve replaced all .longUrl with [longUrl], defining var longUrl = &apos;longUrl&apos;; <br /> <br /> 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. <br /> <br /> it just depends on the size and scale of your plugin.
  • and this is still pretty readable, and it can save you a not insignificant amount of space if your code reuses object properties or strings over and over (and over) again.
  • ok, yeah, well you can do the math. i already did, but it&#x2019;s clear that you don&#x2019;t always want to use this technique. in the last example, we saved 3 bytes. big deal, probably not worth it. <br /> <br /> you can&#x2019;t just &#x201C;phone it in&#x201D; here, you have to actually test and see if what you&#x2019;re doing is making a difference, and determine if it&#x2019;s worth it. make the change, minify, and see if the file is smaller. rinse and repeat. <br /> <br /> or you can figure it out in a generic way, and take notes like i&#x2019;ve done.. but who has that kind of time?
  • so, we&apos;re maybe getting further into the realm of "is this really necessary" here, but this <br /> 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. <br /> <br /> it was a gigantic savings.
  • now, if you were to define this long, space-delimited string as an array (bottom), it would take up 190 chars when minified. here, as a split string, it only takes up 157 chars. not a huge savings (33 characters), but these kinds of things all add up, and since this really doesn&#x2019;t make the code harder to read, it can be helpful. <br /> <br /> in fact, honestly, it&#x2019;s probably easier to read as the string.
  • again, i&#x2019;m not telling you to just DO these things.. but you should consider them, because they might get you thinking about new and creative ways to code, and they might help you define your &#x201C;style&#x201D; <br /> <br /> sure, there&#x2019;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
  • ok. i&#x2019;ve just shown an example where saving a few bytes when minified actually comes at a slight performance cost..
  • beware: .split() will take a not-insignificant amount of time if you call it a lot, so you might not want to call this function repeatedly. in modernizr, it&#x2019;s called just once, but if you needed to call a function like this multiple times, you&apos;d want to perform the `split` in your plugin&apos;s top-level closure, storing the result there, and not in the actual function you&apos;re calling multiple times. <br /> <br /> if you find that you&apos;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&apos;s top-level closure, and not in the individual methods, every time <br /> <br /> plus, then every method would have access to them!
  • and besides, you don&#x2019;t want to be responsible for all those kittens.
  • i&#x2019;m serious. Non-performant code makes Domo hungry. <br /> <br /> i have a real world example though..
  • i recently developed the simplified message style for the os 10 IRC app, linkinus, and one of the things i needed to do was write some kind of linkification script (well, and the longUrl script), since the app doesn&#x2019;t handle that. i needed a LOT of regular expressions (and i still haven&#x2019;t got it completely right, but it&#x2019;s pretty damn close) <br /> <br /> 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. <br /> <br /> 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.
  • here&#x2019;s some code from my url utils plugin. <br /> <br /> it might look gigantic, but it provides a ton of functionality in only 4kb minified. <br /> <br /> and it can be so small because it&#x2019;s very well organized internally, with lots of internal method references instead of `$.whatever` calls.
  • i found that i was testing typeof arg === &#x2018;string&#x2019; or &#x2018;object&#x2019; enough to warrant breaking it into a separate internal, private function. and i actually defined `curry` locally because pretty much all of the functionality in the plugin is exactly the same, with very slightly different behavior between fragment and query string handling. why duplicate code unnecessarily? <br /> <br /> (if you don&#x2019;t know what curry does, you can look it up)
  • here&#x2019;s an example of square-bracket-string instead of dot-methodname as well as curry. <br /> <br /> note, for the sake of the example, i&#x2019;ve stripped out a lot of the comments. the full code and documentation is available online on my site. <br /> <br /> and btw, a plug, this &#x201C;url utils&#x201D; plugin does querystring and fragment parsing, cross-browser fragment page history, url internal / external selectors, etc..
  • and again, the whole thing weighs in at under 4kb minified.
  • v

Transcript

  • 1. BEN ALMAN benalman.com @cowboy JQUERY PLUGIN CREATION organization, generalization, minification, optimization, gratification 'jquery plugin cre: organiz, generaliz, minific, optimiz, gratific'.replace( /([:,]|$)/g, 'ation$1' );
  • 2. A FEW STYLE BASICS
  • 3. WHITE SPACE
  • 4. WHITE SPACE Tabs vs. Spaces
  • 5. WHITE SPACE Tabs vs. Spaces
  • 6. WHITE SPACE Tabs vs. Spaces Less Crowding
  • 7. WHITE SPACE Tabs vs. Spaces Less Crowding
  • 8. COMMENTS Funny can be good, just don’t go overboard.
  • 9. VARIABLES Let them breathe!
  • 10. VARIABLES Let them breathe! Don’t Obfuscate (not yet, at least)
  • 11. A FEW STYLE BASICS Step back and take a look. Is your code readable? If it isn’t now, it certainly won’t be in six months.
  • 12. ORGANIZATION
  • 13. SINGLETON Very basic implementation: Object Literal
  • 14. SINGLETON Everything is public.. Good or Bad?
  • 15. SINGLETON Everything is public.. Good or Bad? Do we really want to expose the “pluralize” method? Does the user want or need this? Do we really want to expose the “things” property? What if the user tries to set it to -100?
  • 16. INTERRUPTION! A brief explanation of Closures
  • 17. CLOSURES Remember this? Banging your head on the table for like, two hours?
  • 18. CLOSURES Remember this? Banging your head on the table Why do they all alert 10?! for like, two hours?
  • 19. CLOSURES Aha! A Closure. (thanks, Google)
  • 20. CLOSURES OMG!! Aha! A Closure. Unicorns & (thanks, Google) Rainbows!!
  • 21. CLOSURES Take Aha! A Closure. your (thanks, Google) pick
  • 22. OK. Back to the the Singleton...
  • 23. SINGLETON Everything is public.. Good or Bad? Do we really want to expose the “pluralize” method? Does the user want or need this? Do we really want to expose the “things” property? What if the user tries to set it to -100?
  • 24. SINGLETON Object Literal (in a Closure)
  • 25. JQUERY PLUGIN The basics
  • 26. JQUERY PLUGIN Closure?
  • 27. JQUERY PLUGIN Closure? Maybe, whatever.
  • 28. JQUERY PLUGIN Closure?
  • 29. JQUERY PLUGIN Closure? Yes!
  • 30. JQUERY PLUGIN $.fn.extend "Extends the jQuery element set to provide new methods"
  • 31. JQUERY PLUGIN Individual functions:
  • 32. JQUERY PLUGIN Individual functions: Less indentation
  • 33. JQUERY PLUGIN Individual functions: Less indentation No pesky commas ( IE)
  • 34. JQUERY PLUGIN Should “lengthen” even be public?
  • 35. JQUERY PLUGIN Should “lengthen” even be public? Probably not.
  • 36. JQUERY PLUGIN Should “lengthen” even be public? Probably not.
  • 37. JQUERY PLUGIN Great, we made a plugin!
  • 38. JQUERY PLUGIN Great, we made a plugin! But can we make it better?
  • 39. GENERALIZATION
  • 40. JQUERY PLUGIN Let’s generalize!
  • 41. JQUERY PLUGIN Holy crap!
  • 42. JQUERY PLUGIN $.longUrl - more flexible
  • 43. JQUERY PLUGIN $.longUrl - more flexible $.fn.longUrl uses it internally
  • 44. DON’T DUPLICATE CODE!
  • 45. JQUERY PLUGIN Generalized!
  • 46. GENERALIZATION
  • 47. GENERALIZATION
  • 48. GENERALIZATION
  • 49. GENERALIZATION
  • 50. GENERALIZATION
  • 51. MINIFICATION
  • 52. JQUERY PLUGIN Unminified
  • 53. JQUERY PLUGIN Minified
  • 54. JQUERY PLUGIN Minified, approximately (slightly more readable)
  • 55. JQUERY PLUGIN Helping the YUI Compressor “bleh”
  • 56. JQUERY PLUGIN Helping the YUI Compressor Objects and references “bleh”
  • 57. JQUERY PLUGIN Helping the YUI Compressor Objects and references “bleh”
  • 58. JQUERY PLUGIN Helping the YUI Compressor Objects and references p_longUrl will be munged
  • 59. right on. JQUERY PLUGIN Helping the YUI Compressor Objects and references p_longUrl will be munged
  • 60. JQUERY PLUGIN Helping the YUI Compressor Objects and Strings
  • 61. neat. JQUERY PLUGIN Helping the YUI Compressor Objects and Strings
  • 62. JQUERY PLUGIN Helping the YUI Compressor Objects and Strings longUrl will be munged
  • 63. JQUERY PLUGIN Helping the YUI Compressor Objects and Strings Yes, I’m insane.
  • 64. JQUERY PLUGIN Helping the YUI Compressor Arrays and Strings
  • 65. JQUERY PLUGIN Helping the YUI Compressor Arrays and Strings 33 chars saved
  • 66. JQUERY PLUGIN Helping the YUI Compressor Arrays and Strings 33 chars saved (in this example)
  • 67. OPTIMIZATION
  • 68. JQUERY PLUGIN Avoid unnecessary overhead!
  • 69. JQUERY PLUGIN Avoid unnecessary overhead! Save a kitten. Optimize.
  • 70. JQUERY PLUGIN Avoid unnecessary overhead! Save a kitten. Optimize.
  • 71. AVOID UNNECESSARY OVERHEAD! This stuff does NOT belong in here!
  • 72. ONE FINAL EXAMPLE (function($) { '$:nomunge'; // Used by YUI compressor. var url_regexp, tag_attributes = {}, // A few constants. undefined, window = this, TRUE = true, FALSE = false, has_onhashchange = 'onhashchange' in window, // Some convenient shortcuts. aps = Array.prototype.slice, loc = document.location, // Internal plugin method references. p_urlTagAttrList, p_urlInternalHost, p_urlInternalRegExp, p_isUrlInternal, p_isUrlExternal, p_urlFilter, p_urlFilterSelector, p_setFragment, // Reused internal strings. str_urlInternal = 'urlInternal', str_urlExternal = 'urlExternal', str_queryString = 'queryString', str_fragment = 'fragment', str_update = 'update', str_passQueryString = 'passQueryString', str_passFragment = 'passFragment', str_fragmentChange = 'fragmentChange', str_hashchange = 'hashchange.' + str_fragmentChange, // fragmentChange event handler timeout_id, last_fragment;
  • 73. ONE FINAL EXAMPLE // A few commonly used bits, broken out to help reduce minified file size. function is_string( arg ) { return typeof arg === 'string'; }; function is_object( arg ) { return typeof arg === 'object'; }; function curry() { var args = aps.call( arguments ), func = args.shift(); return function() { return func.apply( this, args.concat( aps.call( arguments ) ) ); }; }; // Work-around for an annoying Firefox bug where document.location.hash gets // urldecoded by default. function get_fragment() { return loc.href.replace( /^[^#]*#?/, '' ); }; // Method: jQuery.urlTagAttrList // // Get the internal "Default URL attribute per tag" list, or augment the list // with additional tag-attribute pairs, in case the defaults are insufficient. // // This list contains the default attributes for the <jQuery.fn.urlInternal> // and <jQuery.fn.urlExternal> methods, as well as the <:urlInternal> and // <:urlExternal> selector filters, to determine which URL will be tested for // internal- or external-ness. In the <jQuery.fn.queryString>, // <jQuery.fn.fragment>, <jQuery.fn.passQueryString> and // <jQuery.fn.passFragment> methods, this list is used to determine which URL // will be modified. // // Default List: //
  • 74. ONE FINAL EXAMPLE function p_params( fragment_mode, arg0, arg1, arg2 ) { var params; if ( is_string(arg1) || is_object(arg1) ) { // Build URL. return build_url( arg0, arg1, arg2, fragment_mode ); } else if ( is_object(arg0) ) { // Serialize. return $.param( arg0 ); } else if ( is_string(arg0) ) { // Deserialize. return deserialize( arg0, arg1, fragment_mode ); } else { // Deserialize document query string / fragment. params = fragment_mode ? get_fragment() : loc.search; return deserialize( params, arg0, fragment_mode ); } }; $[str_queryString] = curry( p_params, 0 ); $[str_fragment] = curry( p_params, 1 ); function p_fn_params() { var attr, params, merge_mode, args = aps.call( arguments ), fragment_mode = args.shift(); if ( is_string(args[1]) || is_object(args[1]) ) { attr = args.shift(); } params = args.shift(); merge_mode = args.shift();
  • 75. ONE FINAL EXAMPLE /* * URL Utils - v1.11 - 9/10/2009 * http://benalman.com/ * * Copyright (c) 2009 "Cowboy" Ben Alman * Licensed under the MIT license * http://benalman.com/about/license/ */ (function($){var M,q={},z,i=this,u=true,J=false,F="onhashchange" in i,a=Array.prototype.slice,j=document.location,E,d,b,x,v,e,C,I,p="urlInternal",L="urlExternal",y="queryString",G="fragment",H="update",f= "passQueryString",w="passFragment",c="fragmentChange",o="hashchange."+c,h,g;function B(O){return typeof O==="string"}function l(O) {return typeof O==="object"}function m(){var O=a.call(arguments),P=O.shift();return function(){return P.apply(this,O.concat(a.call(arguments)))}}function k(){return j.href.replace(/^[^#]*#?/,"")}$.urlTagAttrList=E=function(O){return $.extend(q,O)};E({a:"href",img:"src",form:"action",base:"href",script:"src",iframe:"src",link:"href"});function r(O){var P=O.nodeName;return P?q[P.toLowerCase()]:""}$.urlInternalHost=d=function(Q){Q=Q?"(?:"+Q+".)?":"";var P=new RegExp("^"+Q +"(.*)","i"),O="^"+j.protocol+"//"+j.hostname.replace(P,Q+"$1")+(j.port?":"+j.port:"")+"/";return b(O)}; $.urlInternalRegExp=b=function(O){if(O){M=B(O)?new RegExp(O,"i"):O}return M};d("www");$.isUrlInternal=x=function(O){if(!O){return z}if(M.test(O)){return u}if(/^https?:///i.test(O)){return J}if(/^(?:#|[a-zd.-]+:)/i.test(O)){return z}return u}; $.isUrlExternal=v=function(O){var P=x(O);return typeof P==="boolean"?!P:P};e=function(P,O){return this.filter(":"+P+(O?"("+O+")":""))}; $.fn[p]=m(e,p);$.fn[L]=m(e,L);C=function(S,R,Q,P){var O=P[3]||r(R);return O?!!S($(R).attr(O)):J};$.expr[":"][p]=m(C,x);$.expr[":"] [L]=m(C,v);function K(Q,R,P,O){var S;if(B(P)||l(P)){return n(R,P,O,Q)}else{if(l(R)){return $.param(R)}else{if(B(R)){return D(R,P,Q)}else{S=Q?k():j.search;return D(S,R,Q)}}}}$[y]=m(K,0);$[G]=m(K,1);function N(){var O,S,R,Q=a.call(arguments),P=Q.shift();if(B(Q[1])||l(Q[1])){O=Q.shift()}S=Q.shift();R=Q.shift();return this.each(function(){var V=$ (this),T=O||r(this),U=T&&V.attr(T)||"";U=K(P,U,S,R);V.attr(T,U)})}$.fn[y]=m(N,0);$.fn[G]=m(N,1);function A(){var Q=a.call(arguments),P=Q.shift(),O=Q.shift(),R=K(P);if($.isFunction(Q[0])){R=Q.shift()(R)}else{if($.isArray(Q[0])) {$.each(Q.shift(),function(T,S){delete R[S]})}}return K(P,O,R,Q.shift())}$[f]=m(A,0);$[w]=m(A,1);function t(){var O,Q=a.call(arguments),P=Q.shift();if(B(Q[0])){O=Q.shift()}return this.each(function(){var T=$(this),R=O|| r(this),S=R&&T.attr(R)||"";S=A.apply(this,[P,S].concat(Q));T.attr(R,S)})}$.fn[f]=m(t,0);$.fn[w]=m(t,1);function D(U,T,Q){var P,W,S,V={},R={"null":null,"true":u,"false":J},O=decodeURIComponent,X=Q?/^.*[#]/:/^.*[?]|#.*$/g;U=U.replace(X,"").replace(/+/g," ").split("&");while(U.length){P=U.shift().split("=");W=O(P[0]);if(P.length===2){S=O(P[1]);if(T){if(S&&!isNaN(S)) {S=Number(S)}else{if(S==="undefined"){S=z}else{if(R[S]!==z){S=R[S]}}}}if($.isArray(V[W])){V[W].push(S)}else{if(V[W]!==z) {V[W]=[V[W],S]}else{V[W]=S}}}else{if(W){V[W]=T?z:""}}}return V}function n(O,Q,T,P){var U,W=P?/^([^#]*)[#]?(.*)$/:/^([^#?]*)[?]?([^#]*) (#?.*)/,S=O.match(W),V=D(S[2],0,P),R=S[3]||"";if(B(Q)){Q=D(Q,0,P)}if(T===2){U=Q}else{if(T===1){U=$.extend({},Q,V)}else{U= $.extend({},V,Q)}}U=$.param(U);return S[1]+(P?"#":U||!S[1]?"?":"")+U+R}$.setFragment=I=function(P,O){var Q=l(P)?K(u,P):(P||"").replace(/ ^#/,"");Q=P?n("#"+k(),"#"+Q,O,1):"#";j.href=j.href.replace(/#.*$/,"")+Q};$[c]=function(O){if(O===u){O=100}function P(){var R= $.Event(c);R[G]=k();$(document).trigger(R)}F&&$(i).unbind(o);h&&clearTimeout(h);h=null;if(typeof O==="number"){if(F){$ (i).bind(o,P)}else{g=k();if($.isFunction(s)){s=s()}(function Q(){var S=k(),R=s[G](g);if(S!==g){s[H](S,R);g=S;P()}else{if(R!==g){I(R, 2)}}h=setTimeout(Q,O<0?0:O)})()}}};function s(){var O,P=$.browser,Q={};Q[H]=Q[G]=function(R){return R};if(P.msie&&P.version<8) {Q[H]=function(T,R){var S=O.document;if(T!==R){S.open();S.close();S.location.hash="#"+T}};Q[G]=function(){return O.document.location.hash.replace(/^#/,"")};O=$("<iframe/>").hide().appendTo("body").get(0).contentWindow;Q[H](k())}return Q}})(jQuery);
  • 76. GRATIFICATION
  • 77. GRATIFICATION Clean, simple API
  • 78. GRATIFICATION Clean, simple API Generalized code makes your life easier
  • 79. GRATIFICATION Clean, simple API Generalized code makes your life easier Minified code makes it smaller
  • 80. GRATIFICATION Clean, simple API Generalized code makes your life easier Minified code makes it smaller Efficient code makes it work faster
  • 81. GRATIFICATION Clean, simple API Generalized code makes your life easier Minified code makes it smaller Efficient code makes it work faster (and people complain less)
  • 82. benalman.com BEN ALMAN @cowboy