Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A template for a jQuery plugin with options and accessible methods?

I am want to build a plugin with accessible methods and options, this for a complex plugin. I need the methods to be accessible outside the plugin because if somebody ads something to the DOM it needs to be updated(so we dont need the run the complete plugin again).

I have seen in the past that there are plugin that do it like this, but I cant find them, so I cant take a look at them. I am still new to javascript so any help would be nice.

It would be nice if we still can globally override the options.

How I want to use the plugin:

// options
$('#someid').myplugin({name: 'hello world'});

// methods(would be nice if we can use this)
$('#someid').myplugin('update');

// my old plugin wrapper

;(function($, window, document, undefined){

    $.fn.pluginmyPlugin = function(options) { 

        options = $.extend({}, $.fn.pluginmyPlugin.options, options); 

            return this.each(function() {  

                var obj = $(this);

                // the code 
            });     
        };

        /**
        * Default settings(dont change).
        * You can globally override these options
        * by using $.fn.pluginName.key = 'value';
        **/
        $.fn.pluginmyPlugin.options = {
            name: '',
                            ...         
        };

})(jQuery, window, document);

Update

So after looking at the jQuery docs I have build the following code, please let me know if there's something wrong with the code, if it can be build better...

;(function($, window, document, undefined){

    var methods = {

        init : function( options ) {

            options = $.extend({}, $.fn.pluginmyPlugin.options, options); 

            return this.each(function(){

            alert('yes i am the main code')

            });
        },
        update : function( ) {
             alert('updated')
        }
    };

    $.fn.pluginmyPlugin = function(method) { 

        if ( methods[method] ) {
          return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
        } else if ( typeof method === 'object' || ! method ) {
          return methods.init.apply( this, arguments );
        } else {
          $.error( 'Method ' +  method + ' does not exist on this plugin' );
        }    

    };

        /**
        * Default settings(dont change).
        * You can globally override these options
        * by using $.fn.pluginName.key = 'value';
        **/
        $.fn.pluginmyPlugin.options = {
            name: 'john doe',
            //....
        };

})(jQuery, window, document);
like image 289
user759235 Avatar asked Dec 08 '12 12:12

user759235


3 Answers

An alternative:

var Plugin = function($self, options) {
  this.$self = $self;
  this.options = $.extend({}, $.fn.plugin.defaults, options);
};

Plugin.prototype.display = function(){
  console.debug("Plugin.display");
};

Plugin.prototype.update = function() {
  console.debug("Plugin.update");
};

$.fn.plugin = function(option) {
  var options = typeof option == "object" && option;

  return this.each(function() {
    var $this = $(this);
    var $plugin = $this.data("plugin");

    if(!$plugin) {
      $plugin = new Plugin($this, options);
      $this.data("plugin", $plugin);
    }

    if (typeof option == 'string') {
      $plugin[option]();
    } else {
      $plugin.display();
    }
  });
};

$.fn.plugin.defaults = {
  propname: "propdefault"
};

Usage:

$("span").plugin({
  propname: "propvalue"
});

$("span").plugin("update");

This absurdly resembles the Twitter Bootstrap's JavaScript template. But, it wasn't completely taking from there. I have a long history of using .data().

Don't forget to wrap it appropriately.

like image 99
Alexander Avatar answered Sep 25 '22 23:09

Alexander


If you want to use the plugin like this:

// Init plugin
$('a').myplugin({
    color: 'blue'
});

// Call the changeBG method
$('a').myplugin('changeBG')
    // chaining
    .each(function () {
        // call the get method href()
        console.log( $(this).myplugin('href') );
    });

or if you need independent Plugin instance per element:

$('a').each(function () {
    $(this).myplugin();
});

you will want to setup your plugin like this:

/*
 *  Project: 
 *  Description: 
 *  Author: 
 *  License: 
 */

// the semi-colon before function invocation is a safety net against concatenated
// scripts and/or other plugins which may not be closed properly.
;(function ( $, window, document, undefined ) {

    // undefined is used here as the undefined global variable in ECMAScript 3 is
    // mutable (ie. it can be changed by someone else). undefined isn't really being
    // passed in so we can ensure the value of it is truly undefined. In ES5, undefined
    // can no longer be modified.

    // window is passed through as local variable rather than global
    // as this (slightly) quickens the resolution process and can be more efficiently
    // minified (especially when both are regularly referenced in your plugin).

    var pluginName = "myplugin",
        // the name of using in .data()
        dataPlugin = "plugin_" + pluginName,
        // default options
        defaults = {
            color: "black"
        };

    function privateMethod () {
        console.log("private method");
    }

    // The actual plugin constructor
    function Plugin() {
        /*
         * Plugin instantiation
         *
         * You already can access element here
         * using this.element
         */
         this.options = $.extend( {}, defaults );
    }

    Plugin.prototype = {

        init: function ( options ) {

            // extend options ( http://api.jquery.com/jQuery.extend/ )
            $.extend( this.options, options );

            /*
             * Place initialization logic here
             */
            this.element.css( 'color', 'red' );
        },

        destroy: function () {
            // unset Plugin data instance
            this.element.data( dataPlugin, null );
        },

        // public get method
        href: function () {
            return this.element.attr( 'href' );
        },

        // public chaining method
        changeBG: function ( color = null ) {
            color = color || this.options['color'];
            return this.element.each(function () {
                // .css() doesn't need .each(), here just for example
                $(this).css( 'background', color );
            });
        }
    }

    /*
     * Plugin wrapper, preventing against multiple instantiations and
     * allowing any public function to be called via the jQuery plugin,
     * e.g. $(element).pluginName('functionName', arg1, arg2, ...)
     */
    $.fn[pluginName] = function ( arg ) {

        var args, instance;

        // only allow the plugin to be instantiated once
        if (!( this.data( dataPlugin ) instanceof Plugin )) {

            // if no instance, create one
            this.data( dataPlugin, new Plugin( this ) );
        }

        instance = this.data( dataPlugin );

        /*
         * because this boilerplate support multiple elements
         * using same Plugin instance, so element should set here
         */
        instance.element = this;

        // Is the first parameter an object (arg), or was omitted,
        // call Plugin.init( arg )
        if (typeof arg === 'undefined' || typeof arg === 'object') {

            if ( typeof instance['init'] === 'function' ) {
                instance.init( arg );
            }

        // checks that the requested public method exists
        } else if ( typeof arg === 'string' && typeof instance[arg] === 'function' ) {

            // copy arguments & remove function name
            args = Array.prototype.slice.call( arguments, 1 );

            // call the method
            return instance[arg].apply( instance, args );

        } else {

            $.error('Method ' + arg + ' does not exist on jQuery.' + pluginName);

        }
    };

}(jQuery, window, document));

Notes:

  • This boilerplate will not use .each() for every method call, you should use .each() when you need
  • Allow re-init plugin, but only 1 instance will be created
  • Example of destroy method is included

Ref: https://github.com/jquery-boilerplate/jquery-boilerplate/wiki/jQuery-boilerplate-and-demo

like image 23
kuzey beytar Avatar answered Sep 24 '22 23:09

kuzey beytar


Have you tried the jQuery UI Widget Factory?

There was a bit of a learning curve, but I love it now, handle the options, and defaults and allows methods, keeps everything wrapped up tight very fancy :)


EDIT

The jQuery UI Widget Factory is a separate component of the jQuery UI Library that provides an easy, object oriented way to create stateful jQuery plugins.

– Introduction to Stateful Plugins and the Widget Factory.

I don't think the extra overhead is much to be worried about in most circumstances. These days I write in coffescript and everything is compiled, minified and gzipped, so a few extra lines here and there doesn't make much of a difference. My research in website speed seems to indicate that the number of HTTP requests is a big deal - indeed the former colleague that put me on this track worked for a browser based game and was all about speed speed speed.

like image 38
CodeMonkey Avatar answered Sep 25 '22 23:09

CodeMonkey