A Convenient Way to Write a jQuery Plugin[source]
xml
<glacius:metadata> | |
<title>A Convenient Way to Write a jQuery Plugin</title> | |
<description>A convenient way to write a jQuery plugin</description> | |
<category>Legacy blog posts</category> | |
<category>Programming</category> | |
<category>JavaScript</category> | |
<category>jQuery</category> | |
</glacius:metadata> | |
<glacius:macro name="legacy blargh banner"> | |
<properties> | |
<originalUrl>https://tmont.com/blargh/2014/1/a-convenient-way-to-write-a-jquery-plugin</originalUrl> | |
<originalDate>2014-01-15T15:18:28.000Z</originalDate> | |
</properties> | |
</glacius:macro> | |
<p> | |
I've written quite a few jQuery plugins in my day. The early ones were horrible | |
mishmashes of spaghetti code interlaced with <code>$.fn</code>. Back then I | |
didn't even know <code>$.fn</code> was supposed to be. I thought <code>fn</code> | |
was just some magic string that did something magical, and then you suddenly | |
had written a jQuery plugin! | |
</p> | |
<p> | |
Since then, for decently sophisticated plugins (i.e. ones that create state) | |
I've started constructing them in a more consistent manner. More specifically, | |
I totally ripped off the format that the <a href="https://getbootstrap.com/">Bootstrap</a> | |
authors used for their plugins, like <code>$('selector').button('reset')</code>. | |
I had to hack the button plugin because for some reason it put every action | |
in the event loop with <code>setTimeout</code> and I couldn't rely on things | |
occurring in the proper order. As I was cursing the authors out for doing something | |
so annoying, I was simultaneously praising them for writing their plugins in a way | |
that was consistent and easy to work with. Even if they don't use semicolons. | |
The heathen bastards. | |
</p> | |
<p> | |
Before I get to the actual format, I want to emphasize that for simple plugins, | |
doing things like this is fine (and probably preferable): | |
</p> | |
<glacius:code lang="javascript"><![CDATA[ | |
$.fn.redOrBlueLol = function() { | |
return this.each(function() { | |
var $this = $(this); | |
$this.css('color', $this.css('color') === 'red' ? 'blue' : 'red'); | |
}); | |
}; | |
]]></glacius:code> | |
<p> | |
If all you're doing is some minor DOM manipulation, then you don't really | |
need to worry about writing your plugin in some consistent format. | |
</p> | |
<h3>The Format</h3> | |
<p> | |
First, you have your stateful object and its prototype: | |
</p> | |
<glacius:code lang="javascript"><![CDATA[ | |
var ns = 'my-plugin'; | |
function MyStatefulObject($element, options) { | |
this.$element = $element; | |
this.doStuff = !!options.doStuff; | |
} | |
MyStatefulObject.prototype = { | |
helloWorld: function(name) { | |
name = name || 'world'; | |
this.$element.text('Hello ' + name + '!'); | |
}, | |
destroy: function() { | |
// stuff to tear down all the mess you've made | |
this.$element.off('.' + ns); | |
} | |
}; | |
]]></glacius:code> | |
<p> | |
Then the actual definition of the plugin: | |
</p> | |
<glacius:code lang="javascript"><![CDATA[ | |
$.fn.myPlugin = function(options) { | |
options = options || {}; | |
return this.each(function() { | |
var $element = $(this), | |
thing = $element.data(ns), | |
method = typeof(options) === 'string' ? options : ''; | |
if (!thing) { | |
var realOptions = !options || typeof(options) !== 'object' ? {} : options; | |
$element.data(ns, (thing = new MyStatefulObject($element, realOptions)); | |
} | |
if (typeof(thing[method]) === 'function') { | |
thing[method].apply(thing, [].slice.call(arguments, 1)); | |
} | |
}); | |
}; | |
]]></glacius:code> | |
<p> | |
And that's it. Now you can use your plugin as such: | |
</p> | |
<glacius:code lang="javascript"><![CDATA[ | |
$('selector').myPlugin('helloWorld'); | |
$('.billy').myPlugin('helloWorld', 'Billy'); | |
$('selector, .billy').myPlugin('destroy'); | |
]]></glacius:code> | |
<p> | |
You can see an example of this in action in an audio player plugin I've | |
been working lately: | |
<a href="https://github.com/tmont/rach3/blob/master/jquery.rach3.js">jquery.rach3.js</a> | |
</p> | |
<p> | |
And that's it. Nothing very complicated or magical about it. Just convenient. | |
</p> | |