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>