3
\$\begingroup\$

Inspired by jqueryboilerplate.com, I extended their boilerplate to fit my needs. Coming from PHP development, I wanted to have a good starting point for writing jQuery plugins with a defined API/interface (more at jquib.web-sav.de).

/**
 * JQUIB - JQUERY INTERFACEPLUGIN BOILERPLATE
 *
 * This is a solid starter template for your jQuery plugins. Its designed
 * to keep all logic and data hidden from public access except the set of
 * functions you defined inside its interface function namespace.
 *
 * Inspired by http://jqueryboilerplate.com
 *
 * @see        http://web-sav.de/jquib-boilerplate
 * @copyright  André Kroll
 * @license    MIT
 */
;
(function ($, window, document, undefined) {
  const PLUGIN_NAME = 'your_plugin';

  const defaults = {
    option_one: 'value_1',
    option_two: 'value_2'
  };

  /* PLUGIN - CONSTRUCTOR AND INSTANCE METHODS ............................... (scope: private) */
  var Plugin = {

    options: {},
    /* ... more plugin properties here ... */

    // Gets called whenever a new plugin instance is created
    __construct: function (options) {
      this.options = this.initOptions(options);
      /* ... you can define more parameters for your constructor                     */
      /*     - the bootstrap process will pass them through                          */

      /* ... start writing your plugin logic ...                                     */
      /*     From here you have access to a plugin                                   */
      /*     .. member with           "this.propertyName;"                           */
      /*     .. method with           "this.methodName();"                           */
      /*     .. interface method with "this.public_interface.interfaceMethodName();" */
      /*     .. helper function with  "helperFunctionName();"                        */
      /*                                                                P e a c e    */
    },

    // Validate options and merge with defaults
    initOptions: function (options) {
      /* ... put your option validation logic here ... */
      return $.extend({}, defaults, options);
    }

  };

  /* PLUGIN - PUBLIC / INTERFACE METHODS ...................................... (scope: public) */
  Plugin.public_fn = {

    // Get the jQuery-wrapped plugin container
    get$: function () {
      return $(this.container);
    }

  };

  /* HELPER FUNCTIONS ------------------------------------------------- (scope: private static) */
  {
    // Simple and safe function to log/debug elements
    function log(elementToLog) {
      if ( window.console && window.console.log ) {
        window.console.log(elementToLog);
      }
    }
  }

  /* BOOTSTRAP AND PLUGIN REGISTRATION ======================================================== */
  {
    // Register the plugin at jQuerys function namespace
    $.fn[PLUGIN_NAME] = function (options) {
      var origArgs = arguments;
      this.each(function () {
        if ( undefined == $.data(this, "plugin_" + PLUGIN_NAME) ) {
          // First call by this element: create new instance of the plugin
          $.data(this, "plugin_" + PLUGIN_NAME, (function (container, options) {
            var plugin = Object.create(Plugin);
            plugin['container'] = container;
            plugin['public_interface'] = {};
            for ( prop in plugin.public_fn ) {
              if ( typeof plugin.public_fn[prop] === 'function' ) {
                // Register all interface functions to call within the plugin object context
                plugin['public_interface'][prop] = $.proxy(plugin.public_fn[prop], plugin);
              }
            }
            plugin.__construct.apply(plugin, origArgs);
            return plugin;
          })(this, options));
        }
      });
      // Return plugin interface of the first element
      return $.data(this.get(0), "plugin_" + PLUGIN_NAME).public_interface;
    };
  }
})(jQuery, window, document);
\$\endgroup\$
1
\$\begingroup\$

Very interesting and fun question. Boilerplate code is a reason to close questions down, but this code is a perfectly valid question.

Naming

  • JavaScript uses lowerCamelCase variable and function names, avoid names like public_interface and option_one.
  • You have anonymous functions, this makes stack-traces needlessly complicated, go for __construct: function pluginConstructor (options) {
  • Prefixing with _ should be avoided. If you do it, ensure _ items are 'private' unlike __construct

Idioms and Custom

  • Yoda conditionals like if ( undefined == $.data(this, "plugin_" + PLUGIN_NAME) ) are hard to read
  • Worse, your conditionals are not consistently Yoda: if ( typeof plugin.public_fn[prop] === 'function' )

Comments

  • I like your approach to commenting

JSHint.com

  • Your have problems in your code:
    • Unnecessary semicolon
    • Declaring functions in an extra set of {} is bad practice
    • Use plugin.container, not plugin['container']
    • prop leaks in to the global namespace here: for ( prop in plugin.public_fn ) {

Finally, having undefined as a parameter is silly. If you were to pass anything but undefined, then your yoda condition would fail.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.