How to create a jQuery plugin with methods?
Asked Answered
E

20

199

I'm trying to write a jQuery plugin that will provide additional functions/methods to the object that calls it. All the tutorials I read online (have been browsing for the past 2 hours) include, at the most, how to add options, but not additional functions.

Here's what I am looking to do:

//format div to be a message container by calling the plugin for that div

$("#mydiv").messagePlugin();
$("#mydiv").messagePlugin().saySomething("hello");

or something along those lines. Here's what it boils down to: I call the plugin, then I call a function associated with that plugin. I can't seem to find a way to do this, and I've seen many plugins do it before.

Here's what I have so far for the plugin:

jQuery.fn.messagePlugin = function() {
  return this.each(function(){
    alert(this);
  });

  //i tried to do this, but it does not seem to work
  jQuery.fn.messagePlugin.saySomething = function(message){
    $(this).html(message);
  }
};

How can I achieve something like that?

Thank you!


Update Nov 18, 2013: I've changed the correct answer to that of Hari's following comments and upvotes.

Eberhard answered 12/7, 2009 at 22:22 Comment(0)
M
321

According to the jQuery Plugin Authoring page (http://docs.jquery.com/Plugins/Authoring), it's best not to muddy up the jQuery and jQuery.fn namespaces. They suggest this method:

(function( $ ){

    var methods = {
        init : function(options) {

        },
        show : function( ) {    },// IS
        hide : function( ) {  },// GOOD
        update : function( content ) {  }// !!!
    };

    $.fn.tooltip = function(methodOrOptions) {
        if ( methods[methodOrOptions] ) {
            return methods[ methodOrOptions ].apply( this, Array.prototype.slice.call( arguments, 1 ));
        } else if ( typeof methodOrOptions === 'object' || ! methodOrOptions ) {
            // Default to "init"
            return methods.init.apply( this, arguments );
        } else {
            $.error( 'Method ' +  methodOrOptions + ' does not exist on jQuery.tooltip' );
        }    
    };


})( jQuery );

Basically you store your functions in an array (scoped to the wrapping function) and check for an entry if the parameter passed is a string, reverting to a default method ("init" here) if the parameter is an object (or null).

Then you can call the methods like so...

$('div').tooltip(); // calls the init method
$('div').tooltip({  // calls the init method
  foo : 'bar'
});
$('div').tooltip('hide'); // calls the hide method
$('div').tooltip('update', 'This is the new tooltip content!'); // calls the update method

Javascripts "arguments" variable is an array of all the arguments passed so it works with arbitrary lengths of function parameters.

Metaphrast answered 29/7, 2011 at 10:33 Comment(11)
This is the method I use. You can also call the methods staticly via $.fn.tooltip('methodname', params);Screech
For this interested in a more javascript class-like version of the jQuery plugin template have a look here: club15cc.com/post/11690384272/… Amongst other things, it improves on the jquery.com version by allowing for class properties which maintain state been method calls.Metaphrast
Very handy architecture. I also added this line before calling the init method: this.data('tooltip', $.extend(true, {}, $.fn.tooltip.defaults, methodOrOptions));, so now I can access to options whenever I want after the initialization.Blague
For any like me who first said "where did the arguments variable come from" - developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… - I've been using JS forever and never knew that. You learn something new everyday!Ivette
Assuming I define my configuration defaults (which can be overwritten by passing object on plugin init), how do I go about accessing their values? Or do I have to create methods/getters for each property?Kenelm
@DiH, I'm with you on this one. This approach seems great, but it doesn't give you access to your global settings from anywhere other than init.Lathing
There's a major problem with this technique! It doesn't create a new instance for every element in the selector like you think you're doing, instead it creates only a single instance attached to the selector itself. View my answer for a solution.Margarettamargarette
@KevinJurkowski - Yes and no - You are correct that this method does not create a new instance for each element but it is not always (I'd say most of the time not) necessary to have a separate instance for each element. @HariKaramSingh - QUESTION: Why would you have methods object scoped to the wrapping function as opposed to having it inside the plugin body?Examinant
Not my decision. That code is from jQuery's site (or least the version from several years ago!). jQuery didn't seem concerned with having an object-based state as is typical in OOP. That's why I made this more complex but stateful version: gist.github.com/Club15CC/1300890 It's been a LONG time since I've looked at this so please be kind!Metaphrast
As stated in mozilla documentation: You should not slice on arguments because it prevents optimizations in JavaScript engines (V8 for example). Instead, try constructing a new array by iterating through the arguments object. More information.Mame
How should methods access the options?Verdure
V
56

Here's the pattern I have used for creating plugins with additional methods. You would use it like:

$('selector').myplugin( { key: 'value' } );

or, to invoke a method directly,

$('selector').myplugin( 'mymethod1', 'argument' );

Example:

;(function($) {

    $.fn.extend({
        myplugin: function(options,arg) {
            if (options && typeof(options) == 'object') {
                options = $.extend( {}, $.myplugin.defaults, options );
            }

            // this creates a plugin for each element in
            // the selector or runs the function once per
            // selector.  To have it do so for just the
            // first element (once), return false after
            // creating the plugin to stop the each iteration 
            this.each(function() {
                new $.myplugin(this, options, arg );
            });
            return;
        }
    });

    $.myplugin = function( elem, options, arg ) {

        if (options && typeof(options) == 'string') {
           if (options == 'mymethod1') {
               myplugin_method1( arg );
           }
           else if (options == 'mymethod2') {
               myplugin_method2( arg );
           }
           return;
        }

        ...normal plugin actions...

        function myplugin_method1(arg)
        {
            ...do method1 with this and arg
        }

        function myplugin_method2(arg)
        {
            ...do method2 with this and arg
        }

    };

    $.myplugin.defaults = {
       ...
    };

})(jQuery);
Valdemar answered 12/7, 2009 at 22:49 Comment(21)
same pattern as jquery-ui, i dont like all the magic strings but is there any other way!Germanous
this seems like a non-standard way of doing things - is there anything simpler than this, like chaining functions? thank you!Eberhard
also, how would i pass more than one argument under the arg - as an object, perhaps? (i.e. $('selector').myplugin('mymethod1', {arg1 : 'arg1value', arg2 : 'arg2value}) ). thank you!Eberhard
one potential problem(?) that i noticed: when creating the new plugin new $.myplugin(this, options, arg), you pass 3 arguments, while the plugin itself seems to only expect 2 (i.e $.myplugin = function( options, arg)) - is this done on purpose? if not, how do i fix it? thanks!Eberhard
@yuval -- typically jQuery plugins return jQuery or a value, not the plugin itself. That's why the name of the method is passed as an argument to the plugin when you want to invoke the plugin. You could pass any number of arguments, but you'll have to adjust the functions and the argument parsing. Probably best to set them in an anonymous object as you showed.Valdemar
@yuval -- a transliteration mistake when extracting the code from an actual plugin I have. I've corrected it.Valdemar
@tvanfosson: I've created a jsfiddle using the above template. However, a get() method doesn't return the value of an internal variable. Could you tell me what I'm doing wrong? jsfiddle.net/aditya/FCwRsFodder
@Fodder (1) the variable isn't defined at the point in the function that you are invoking the get() function. (2) you don't want to define the variable in the function or it will get reinitialized each time the function is invoked. It's probably better to define it on the plugin itself and reference it. See this updated fiddle: jsfiddle.net/JxqHFValdemar
@Fodder or better yet, store it as data on the element: jsfiddle.net/CmmrSValdemar
@Valdemar In that case, isn't https://mcmap.net/q/66357/-how-to-create-a-jquery-plugin-with-methods/56121 a cleaner way of doing it using jQuery .data() rather than adding static vars to your function?Fodder
@Fodder that's what I did in my second fiddle. It would really depend on whether it's a global plugin option or an option specific to a particular instance of a plugin. If global, like the defaults, adding it to the plugin definition is appropriate. If it's specific to that instance, storing it as data on the element is a better way to go. The approach for functions could be done either using the array technique or inline within the function definition. Either way they're scoped local to the plugin.Valdemar
What is the meaning of ; in your first line? please explain to me :)Glucose
@Glucose it just makes sure that we're starting a new statement so that our function definition isn't interpreted as an argument to someone else's poorly formatted Javascript (i.e, the initial paren isn't taken as a function invocation operator). See #7365672Valdemar
When inside a "myplugin_method", how can I have access to the var of the plugin ?Rifkin
@BabyAzerty - not quite sure what you mean. Any variable declared within the $.myplugin function should be available (within scope) of any functions also defined within it.Valdemar
In the $.myplugin.defaut, I have 2 var (data and height). How can I have access to these var within the plugin's methods ? I tried "height", "myplugin.height","$.myplugin.height" none work...Rifkin
@BabyAzerty defaults.data or $.myplugin.defaults.data, similarly with height, although, these actually get stuff into options so you could use options.data and options.height if you want the, possibly, user-overridden versions.Valdemar
I tried accessing options inside my plugin methods, but I cannot get any variables (data nor height). Here is a fiddle jsfiddle.net/uWxcfRifkin
@Valdemar What do you think about my aproach? Do you know any drawback? https://mcmap.net/q/66357/-how-to-create-a-jquery-plugin-with-methodsNewark
This is very bad architecture. It is error prone, can not be strong typed (with TypeScript) or checked for integrity at compile time. There is a reason why we don't write code in strings.Coppinger
hi guys, how do i call myplugin_method1(arg) in the example?Rociorock
B
35

What about this approach:

jQuery.fn.messagePlugin = function(){
    var selectedObjects = this;
    return {
             saySomething : function(message){
                              $(selectedObjects).each(function(){
                                $(this).html(message);
                              });
                              return selectedObjects; // Preserve the jQuery chainability 
                            },
             anotherAction : function(){
                               //...
                               return selectedObjects;
                             }
           };
}
// Usage:
$('p').messagePlugin().saySomething('I am a Paragraph').css('color', 'red');

The selected objects are stored in the messagePlugin closure, and that function returns an object that contains the functions associated with the plugin, the in each function you can perform the desired actions to the currently selected objects.

You can test and play with the code here.

Edit: Updated code to preserve the power of the jQuery chainability.

Boneyard answered 12/7, 2009 at 23:27 Comment(7)
i am having a bit of a hard time understanding what this would look like. Assuming i have code that needs to be executed the first time this is run, i'll have to first initialize it in my code - something like this: $('p').messagePlugin(); then later in the code i'd like to call the function saySomething like this $('p').messagePlugin().saySomething('something'); will this not re-initialize the plugin and then call the function? what would this look like with the enclosure and options? thank you very much. -yuvalEberhard
Sort of breaks the chainability paradigm of jQuery, though.Valdemar
maybe this should be the best answerInvertebrate
Every time you call messagePlugin() it will create a new object with those two functions, no?Occam
The main issue with this approach is that it cannot preserve chainability following $('p').messagePlugin() unless you call one of the two functions it returns.Alcoholism
@CMS, Please, I have another problem here at Stackoverflow, with chain method; but even using your recipe (returning the selectedObjects variable), it not works, see here a jsfiddle.Countryfied
I am also using the same approach. Just wondering if we can default one method? Is it possible? Ex $('p').messagePlugin() should call saySomething directly. However 2nd method needs to be called as given below. $('p').messagePlugin().anotherAction(). Thanks.Highway
M
20

The problem with the currently selected answer is that you're not actually creating a new instance of the custom plugin for every element in the selector like you think you're doing... you're actually only creating a single instance and passing in the selector itself as the scope.

View this fiddle for a deeper explanation.

Instead, you'll need to loop through the selector using jQuery.each and instantiate a new instance of the custom plugin for every element in the selector.

Here's how:

(function($) {

    var CustomPlugin = function($el, options) {

        this._defaults = {
            randomizer: Math.random()
        };

        this._options = $.extend(true, {}, this._defaults, options);

        this.options = function(options) {
            return (options) ?
                $.extend(true, this._options, options) :
                this._options;
        };

        this.move = function() {
            $el.css('margin-left', this._options.randomizer * 100);
        };

    };

    $.fn.customPlugin = function(methodOrOptions) {

        var method = (typeof methodOrOptions === 'string') ? methodOrOptions : undefined;

        if (method) {
            var customPlugins = [];

            function getCustomPlugin() {
                var $el          = $(this);
                var customPlugin = $el.data('customPlugin');

                customPlugins.push(customPlugin);
            }

            this.each(getCustomPlugin);

            var args    = (arguments.length > 1) ? Array.prototype.slice.call(arguments, 1) : undefined;
            var results = [];

            function applyMethod(index) {
                var customPlugin = customPlugins[index];

                if (!customPlugin) {
                    console.warn('$.customPlugin not instantiated yet');
                    console.info(this);
                    results.push(undefined);
                    return;
                }

                if (typeof customPlugin[method] === 'function') {
                    var result = customPlugin[method].apply(customPlugin, args);
                    results.push(result);
                } else {
                    console.warn('Method \'' + method + '\' not defined in $.customPlugin');
                }
            }

            this.each(applyMethod);

            return (results.length > 1) ? results : results[0];
        } else {
            var options = (typeof methodOrOptions === 'object') ? methodOrOptions : undefined;

            function init() {
                var $el          = $(this);
                var customPlugin = new CustomPlugin($el, options);

                $el.data('customPlugin', customPlugin);
            }

            return this.each(init);
        }

    };

})(jQuery);

And a working fiddle.

You'll notice how in the first fiddle, all divs are always moved to the right the exact same number of pixels. That is because only one options object exists for all elements in the selector.

Using the technique written above, you'll notice that in the second fiddle, each div is not aligned and is randomly moved (excluding the first div as it's randomizer is always set to 1 on line 89). That is because we are now properly instantiating a new custom plugin instance for every element in the selector. Every element has its own options object and is not saved in the selector, but in the instance of the custom plugin itself.

This means that you'll be able to access the methods of the custom plugin instantiated on a specific element in the DOM from new jQuery selectors and aren't forced to cache them, as you would be in the first fiddle.

For example, this would return an array of all options objects using the technique in the second fiddle. It would return undefined in the first.

$('div').customPlugin();
$('div').customPlugin('options'); // would return an array of all options objects

This is how you would have to access the options object in the first fiddle, and would only return a single object, not an array of them:

var divs = $('div').customPlugin();
divs.customPlugin('options'); // would return a single options object

$('div').customPlugin('options');
// would return undefined, since it's not a cached selector

I'd suggest using the technique above, not the one from the currently selected answer.

Margarettamargarette answered 10/4, 2014 at 1:12 Comment(2)
Thanks, this helped me a lot, particularly introducing the .data() method to me. Very handy. FWIW you can also simplify some of your code by using anonymous methods.Dictator
jQuery chainability is not working using this method... $('.my-elements').find('.first-input').customPlugin('update'‌​, 'first value').end().find('.second-input').customPlugin('update', 'second value'); returns Cannot read property 'end' of undefined. jsfiddle.net/h8v1k2pLComitia
T
15

Use the jQuery UI Widget Factory.

Example:

$.widget( "myNamespace.myPlugin", {

    options: {
        // Default options
    },
 
    _create: function() {
        // Initialization logic here
    },
 
    // Create a public method.
    myPublicMethod: function( argument ) {
        // ...
    },

    // Create a private method.
    _myPrivateMethod: function( argument ) {
        // ...
    }
 
});

Initialization:

$('#my-element').myPlugin();
$('#my-element').myPlugin( {defaultValue:10} );

Method calling:

$('#my-element').myPlugin('myPublicMethod', 20);

(This is how the jQuery UI library is built.)

Tight answered 28/7, 2013 at 18:8 Comment(3)
a) that's argumentum ad populum, b) every better JS IDE has code completion or linting, c) google itCoppinger
That is pure delusion, Mr. Sedlacek.Charcot
Per docs: This system is called the Widget Factory and is exposed as jQuery.widget as part of jQuery UI 1.8; however, it can be used independently of jQuery UI. How is $.widget used without jQuery UI?Lewandowski
S
13

A simpler approach is to use nested functions. Then you can chain them in an object-oriented fashion. Example:

jQuery.fn.MyPlugin = function()
{
  var _this = this;
  var a = 1;

  jQuery.fn.MyPlugin.DoSomething = function()
  {
    var b = a;
    var c = 2;

    jQuery.fn.MyPlugin.DoSomething.DoEvenMore = function()
    {
      var d = a;
      var e = c;
      var f = 3;
      return _this;
    };

    return _this;
  };

  return this;
};

And here's how to call it:

var pluginContainer = $("#divSomeContainer");
pluginContainer.MyPlugin();
pluginContainer.MyPlugin.DoSomething();
pluginContainer.MyPlugin.DoSomething.DoEvenMore();

Be careful though. You cannot call a nested function until it has been created. So you cannot do this:

var pluginContainer = $("#divSomeContainer");
pluginContainer.MyPlugin();
pluginContainer.MyPlugin.DoSomething.DoEvenMore();
pluginContainer.MyPlugin.DoSomething();

The DoEvenMore function doesn't even exist because the DoSomething function hasn't been run yet which is required to create the DoEvenMore function. For most jQuery plugins, you really are only going to have one level of nested functions and not two as I've shown here.
Just make sure that when you create nested functions that you define these functions at the beginning of their parent function before any other code in the parent function gets executed.

Finally, note that the "this" member is stored in a variable called "_this". For nested functions, you should return "_this" if you need a reference to the instance in the calling client. You cannot just return "this" in the nested function because that will return a reference to the function and not the jQuery instance. Returning a jQuery reference allows you to chain intrinsic jQuery methods on return.

Swop answered 26/6, 2010 at 9:38 Comment(3)
This is great - I only wonder why jQuery seems to favor calling the methods by name as in the .plugin('method') pattern?Occam
This does not work. If you invoke the plugin on two different containers, the internal variables get overridden (namely _this)Shandashandee
Fail: does not allow pluginContainer.MyPlugin.DoEvenMore().DoSomething();Fleshly
S
9

I got it from jQuery Plugin Boilerplate

Also described in jQuery Plugin Boilerplate, reprise

// jQuery Plugin Boilerplate
// A boilerplate for jumpstarting jQuery plugins development
// version 1.1, May 14th, 2011
// by Stefan Gabos

// remember to change every instance of "pluginName" to the name of your plugin!
(function($) {

    // here we go!
    $.pluginName = function(element, options) {

    // plugin's default options
    // this is private property and is accessible only from inside the plugin
    var defaults = {

        foo: 'bar',

        // if your plugin is event-driven, you may provide callback capabilities
        // for its events. execute these functions before or after events of your
        // plugin, so that users may customize those particular events without
        // changing the plugin's code
        onFoo: function() {}

    }

    // to avoid confusions, use "plugin" to reference the
    // current instance of the object
    var plugin = this;

    // this will hold the merged default, and user-provided options
    // plugin's properties will be available through this object like:
    // plugin.settings.propertyName from inside the plugin or
    // element.data('pluginName').settings.propertyName from outside the plugin,
    // where "element" is the element the plugin is attached to;
    plugin.settings = {}

    var $element = $(element), // reference to the jQuery version of DOM element
    element = element; // reference to the actual DOM element

    // the "constructor" method that gets called when the object is created
    plugin.init = function() {

    // the plugin's final properties are the merged default and
    // user-provided options (if any)
    plugin.settings = $.extend({}, defaults, options);

    // code goes here

   }

   // public methods
   // these methods can be called like:
   // plugin.methodName(arg1, arg2, ... argn) from inside the plugin or
   // element.data('pluginName').publicMethod(arg1, arg2, ... argn) from outside
   // the plugin, where "element" is the element the plugin is attached to;

   // a public method. for demonstration purposes only - remove it!
   plugin.foo_public_method = function() {

   // code goes here

    }

     // private methods
     // these methods can be called only from inside the plugin like:
     // methodName(arg1, arg2, ... argn)

     // a private method. for demonstration purposes only - remove it!
     var foo_private_method = function() {

        // code goes here

     }

     // fire up the plugin!
     // call the "constructor" method
     plugin.init();

     }

     // add the plugin to the jQuery.fn object
     $.fn.pluginName = function(options) {

        // iterate through the DOM elements we are attaching the plugin to
        return this.each(function() {

          // if plugin has not already been attached to the element
          if (undefined == $(this).data('pluginName')) {

              // create a new instance of the plugin
              // pass the DOM element and the user-provided options as arguments
              var plugin = new $.pluginName(this, options);

              // in the jQuery version of the element
              // store a reference to the plugin object
              // you can later access the plugin and its methods and properties like
              // element.data('pluginName').publicMethod(arg1, arg2, ... argn) or
              // element.data('pluginName').settings.propertyName
              $(this).data('pluginName', plugin);

           }

        });

    }

})(jQuery);
Savill answered 23/6, 2014 at 5:55 Comment(2)
Your method breaks jQuery chaining: $('.first-input').data('pluginName').publicMethod('new value').css('color', red); returns Cannot read property 'css' of undefined jsfiddle.net/h8v1k2pL/1Comitia
@AlexG given this example you would add return $element so in this example you would change it to plugin.foo_public_method = function() {/* Your Code */ return $element;} @Salim thanks for helping me ... github.com/AndreaLombardo/BootSideMenu/pull/34Zak
P
6

Too late but maybe it can help someone one day.

I was in the same situation like, creating a jQuery plugin with some methods, and after reading some articles and some tires I create a jQuery plugin boilerplate (https://github.com/acanimal/jQuery-Plugin-Boilerplate).

In addition, I develop with it a plugin to manage tags (https://github.com/acanimal/tagger.js) and wrote a two blog posts explaining step by step the creation of a jQuery plugin (https://www.acuriousanimal.com/blog/20130115/things-i-learned-creating-a-jquery-plugin-part-i).

Predigestion answered 10/5, 2014 at 18:6 Comment(2)
possibly the best post I have come across yet about creating jQuery plugins as a beginner - thanks ;)Weeping
Thank you for this SANE approach. Creating jQuery plugins correctly is just way too ridiculously complicated. This really is a major weakness of jQuery and of Javascript as a whole with it's prototype weirdness.Fuld
O
5

You can do:

(function($) {
  var YourPlugin = function(element, option) {
    var defaults = {
      //default value
    }

    this.option = $.extend({}, defaults, option);
    this.$element = $(element);
    this.init();
  }

  YourPlugin.prototype = {
    init: function() { },
    show: function() { },
    //another functions
  }

  $.fn.yourPlugin = function(option) {
    var arg = arguments,
        options = typeof option == 'object' && option;;
    return this.each(function() {
      var $this = $(this),
          data = $this.data('yourPlugin');

      if (!data) $this.data('yourPlugin', (data = new YourPlugin(this, options)));
      if (typeof option === 'string') {
        if (arg.length > 1) {
          data[option].apply(data, Array.prototype.slice.call(arg, 1));
        } else {
          data[option]();
        }
      }
    });
  };
});

In this way your plugins object is stored as data value in your element.

//Initialization without option
$('#myId').yourPlugin();

//Initialization with option
$('#myId').yourPlugin({
  // your option
});

// call show method
$('#myId').yourPlugin('show');
Obedient answered 7/3, 2016 at 6:28 Comment(0)
P
3

What about using triggers? Does anyone know any drawback using them? The benefit is that all internal variables are accessible via the triggers, and the code is very simple.

See on jsfiddle.

Example usage

<div id="mydiv">This is the message container...</div>

<script>
    var mp = $("#mydiv").messagePlugin();

    // the plugin returns the element it is called on
    mp.trigger("messagePlugin.saySomething", "hello");

    // so defining the mp variable is not needed...
    $("#mydiv").trigger("messagePlugin.repeatLastMessage");
</script>

Plugin

jQuery.fn.messagePlugin = function() {

    return this.each(function() {

        var lastmessage,
            $this = $(this);

        $this.on('messagePlugin.saySomething', function(e, message) {
            lastmessage = message;
            saySomething(message);
        });

        $this.on('messagePlugin.repeatLastMessage', function(e) {
            repeatLastMessage();
        });

        function saySomething(message) {
            $this.html("<p>" + message + "</p>");
        }

        function repeatLastMessage() {
            $this.append('<p>Last message was: ' + lastmessage + '</p>');
        }

    });

}
Pickaninny answered 27/8, 2013 at 0:48 Comment(2)
cf. your comment. The only problem I see here is arguably a misuse of the event system. It's atypical to use events purely to invoke a function; it seems like overkill and may be easily broken. Normally, you'd use events in a publish-subscribe fashion, e.g., a function publishes that some condition "A" has occurred. Other entities, interested in "A", listen for the message that "A" has happened, then do something. You seem to be using it as push "command" instead, but assuming there is only one listener. You'd want to be careful that your semantics aren't broken by (others) adding listeners.Valdemar
@Valdemar Thanks for your comment. I understand that it is not common technique and it can cause problems if someone accidentally add an event listener, but if it is named after the plugin, then it is very unlikely. I don't know about any performance related issues, but the code itself seems to be much simpler to me than with the other solutions, but I may be missing something.Newark
S
3

Here I want to suggest steps to create simple plugin with arguments.

(function($) {
  $.fn.myFirstPlugin = function(options) {
    // Default params
    var params = $.extend({
      text     : 'Default Title',
      fontsize : 10,
    }, options);
    return $(this).text(params.text);
  }
}(jQuery));

$('.cls-title').myFirstPlugin({ text : 'Argument Title' });
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<h1 class="cls-title"></h1>

Here, we have added default object called params and set default values of options using extend function. Hence, If we pass blank argument then it will set default values instead otherwise it will set.

Read more: How to Create JQuery plugin

Skiascope answered 7/4, 2016 at 6:8 Comment(3)
Hi Gopal Joshi , Please give next level jquery plugin creation. we expect from your needful answer.Legionary
Hello @SakthiKarthik, Off cource I will publish new tutorial soon in my blogSkiascope
Hi @SakthiKarthik, You may refer new article on next level jquery plugin here sgeek.org/…Skiascope
H
2

Try this one:

$.fn.extend({
"calendar":function(){
    console.log(this);
    var methods = {
            "add":function(){console.log("add"); return this;},
            "init":function(){console.log("init"); return this;},
            "sample":function(){console.log("sample"); return this;}
    };

    methods.init(); // you can call any method inside
    return methods;
}}); 
$.fn.calendar() // caller or 
$.fn.calendar().sample().add().sample() ......; // call methods
Hypothyroidism answered 30/6, 2014 at 17:42 Comment(0)
C
1

Here is my bare-bones version of this. Similar to the ones posted before, you would call like:

$('#myDiv').MessagePlugin({ yourSettings: 'here' })
           .MessagePlugin('saySomething','Hello World!');

-or access the instance directly @ plugin_MessagePlugin

$elem = $('#myDiv').MessagePlugin();
var instance = $elem.data('plugin_MessagePlugin');
instance.saySomething('Hello World!');

MessagePlugin.js

;(function($){

    function MessagePlugin(element,settings){ // The Plugin
        this.$elem = element;
        this._settings = settings;
        this.settings = $.extend(this._default,settings);
    }

    MessagePlugin.prototype = { // The Plugin prototype
        _default: {
            message: 'Generic message'
        },
        initialize: function(){},
        saySomething: function(message){
            message = message || this._default.message;
            return this.$elem.html(message);
        }
    };

    $.fn.MessagePlugin = function(settings){ // The Plugin call

        var instance = this.data('plugin_MessagePlugin'); // Get instance

        if(instance===undefined){ // Do instantiate if undefined
            settings = settings || {};
            this.data('plugin_MessagePlugin',new MessagePlugin(this,settings));
            return this;
        }

        if($.isFunction(MessagePlugin.prototype[settings])){ // Call method if argument is name of method
            var args = Array.prototype.slice.call(arguments); // Get the arguments as Array
            args.shift(); // Remove first argument (name of method)
            return MessagePlugin.prototype[settings].apply(instance, args); // Call the method
        }

        // Do error handling

        return this;
    }

})(jQuery);
Cooncan answered 2/8, 2014 at 1:0 Comment(0)
L
1

The following plugin-structure utilizes the jQuery-data()-method to provide a public interface to internal plugin-methods/-settings (while preserving jQuery-chainability):

(function($, window, undefined) { 
  const defaults = {
    elementId   : null,
    shape       : "square",
    color       : "aqua",
    borderWidth : "10px",
    borderColor : "DarkGray"
  };

  $.fn.myPlugin = function(options) {
    // settings, e.g.:  
    var settings = $.extend({}, defaults, options);

    // private methods, e.g.:
    var setBorder = function(color, width) {        
      settings.borderColor = color;
      settings.borderWidth = width;          
      drawShape();
    };

    var drawShape = function() {         
      $('#' + settings.elementId).attr('class', settings.shape + " " + "center"); 
      $('#' + settings.elementId).css({
        'background-color': settings.color,
        'border': settings.borderWidth + ' solid ' + settings.borderColor      
      });
      $('#' + settings.elementId).html(settings.color + " " + settings.shape);            
    };

    return this.each(function() { // jQuery chainability     
      // set stuff on ini, e.g.:
      settings.elementId = $(this).attr('id'); 
      drawShape();

      // PUBLIC INTERFACE 
      // gives us stuff like: 
      //
      //    $("#...").data('myPlugin').myPublicPluginMethod();
      //
      var myPlugin = {
        element: $(this),
        // access private plugin methods, e.g.: 
        setBorder: function(color, width) {        
          setBorder(color, width);
          return this.element; // To ensure jQuery chainability 
        },
        // access plugin settings, e.g.: 
        color: function() {
          return settings.color;
        },        
        // access setting "shape" 
        shape: function() {
          return settings.shape;
        },     
        // inspect settings 
        inspectSettings: function() {
          msg = "inspecting settings for element '" + settings.elementId + "':";   
          msg += "\n--- shape: '" + settings.shape + "'";
          msg += "\n--- color: '" + settings.color + "'";
          msg += "\n--- border: '" + settings.borderWidth + ' solid ' + settings.borderColor + "'";
          return msg;
        },               
        // do stuff on element, e.g.:  
        change: function(shape, color) {        
          settings.shape = shape;
          settings.color = color;
          drawShape();   
          return this.element; // To ensure jQuery chainability 
        }
      };
      $(this).data("myPlugin", myPlugin);
    }); // return this.each 
  }; // myPlugin
}(jQuery));

Now you can call internal plugin-methods to access or modify plugin data or the relevant element using this syntax:

$("#...").data('myPlugin').myPublicPluginMethod(); 

As long as you return the current element (this) from inside your implementation of myPublicPluginMethod() jQuery-chainability will be preserved - so the following works:

$("#...").data('myPlugin').myPublicPluginMethod().css("color", "red").html("...."); 

Here are some examples (for details checkout this fiddle):

// initialize plugin on elements, e.g.:
$("#shape1").myPlugin({shape: 'square', color: 'blue', borderColor: 'SteelBlue'});
$("#shape2").myPlugin({shape: 'rectangle', color: 'red', borderColor: '#ff4d4d'});
$("#shape3").myPlugin({shape: 'circle', color: 'green', borderColor: 'LimeGreen'});

// calling plugin methods to read element specific plugin settings:
console.log($("#shape1").data('myPlugin').inspectSettings());    
console.log($("#shape2").data('myPlugin').inspectSettings());    
console.log($("#shape3").data('myPlugin').inspectSettings());      

// calling plugin methods to modify elements, e.g.:
// (OMG! And they are chainable too!) 
$("#shape1").data('myPlugin').change("circle", "green").fadeOut(2000).fadeIn(2000);      
$("#shape1").data('myPlugin').setBorder('LimeGreen', '30px');

$("#shape2").data('myPlugin').change("rectangle", "red"); 
$("#shape2").data('myPlugin').setBorder('#ff4d4d', '40px').css({
  'width': '350px',
  'font-size': '2em' 
}).slideUp(2000).slideDown(2000);              

$("#shape3").data('myPlugin').change("square", "blue").fadeOut(2000).fadeIn(2000);   
$("#shape3").data('myPlugin').setBorder('SteelBlue', '30px');

// etc. ...     
Lylelyles answered 23/1, 2018 at 14:12 Comment(0)
R
0

This can actually be made to work in a "nice" way using defineProperty. Where "nice" means without having to use () to get plugin namespace nor having to pass function name by string.

Compatibility nit: defineProperty doesn't work in ancient browsers such as IE8 and below. Caveat: $.fn.color.blue.apply(foo, args) won't work, you need to use foo.color.blue.apply(foo, args).

function $_color(color)
{
    return this.css('color', color);
}

function $_color_blue()
{
    return this.css('color', 'blue');
}

Object.defineProperty($.fn, 'color',
{
    enumerable: true,
    get: function()
    {
        var self = this;

        var ret = function() { return $_color.apply(self, arguments); }
        ret.blue = function() { return $_color_blue.apply(self, arguments); }

        return ret;
    }
});

$('#foo').color('#f00');
$('#bar').color.blue();

JSFiddle link

Ramie answered 5/9, 2014 at 13:31 Comment(0)
D
0

According to jquery standard you can create plugin as follow:

(function($) {

    //methods starts here....
    var methods = {
        init : function(method,options) {
             this.loadKeywords.settings = $.extend({}, this.loadKeywords.defaults, options);
             methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
             $loadkeywordbase=$(this);
        },
        show : function() {
            //your code here.................
        },
        getData : function() {
           //your code here.................
        }

    } // do not put semi colon here otherwise it will not work in ie7
    //end of methods

    //main plugin function starts here...
    $.fn.loadKeywords = function(options,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 ecw-Keywords');
        }
    };
    $.fn.loadKeywords.defaults = {
            keyName:     'Messages',
            Options:     '1',
            callback: '',
    };
    $.fn.loadKeywords.settings = {};
    //end of plugin keyword function.

})(jQuery);

How to call this plugin?

1.$('your element').loadKeywords('show',{'callback':callbackdata,'keyName':'myKey'}); // show() will be called

Reference: link

Dunnage answered 27/3, 2015 at 6:2 Comment(0)
M
0

I think this might help you...

(function ( $ ) {
  
    $.fn.highlight = function( options ) {
  
        // This is the easiest way to have default options.
        var settings = $.extend({
            // These are the defaults.
            color: "#000",
            backgroundColor: "yellow"
        }, options );
  
        // Highlight the collection based on the settings variable.
        return this.css({
            color: settings.color,
            backgroundColor: settings.backgroundColor
        });
  
    };
  
}( jQuery ));

In the above example i had created a simple jquery highlight plugin.I had shared an article in which i had discussed about How to Create Your Own jQuery Plugin from Basic to Advance. I think you should check it out... http://mycodingtricks.com/jquery/how-to-create-your-own-jquery-plugin/

M16 answered 7/4, 2015 at 22:0 Comment(0)
S
0

Following is a small plug-in to have warning method for debugging purpose. Keep this code in jquery.debug.js file: JS:

jQuery.fn.warning = function() {
   return this.each(function() {
      alert('Tag Name:"' + $(this).prop("tagName") + '".');
   });
};

HTML:

<html>
   <head>
      <title>The jQuery Example</title>

      <script type = "text/javascript" 
         src = "http://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>

      <script src = "jquery.debug.js" type = "text/javascript"></script>

      <script type = "text/javascript" language = "javascript">
         $(document).ready(function() {
            $("div").warning();
            $("p").warning();
         });
      </script> 
   </head>

   <body>
      <p>This is paragraph</p>
      <div>This is division</div>
   </body>

</html>
Statued answered 16/2, 2016 at 8:11 Comment(0)
S
0

Here is how I do it:

(function ( $ ) {

$.fn.gridview = function( options ) {

    ..........
    ..........


    var factory = new htmlFactory();
    factory.header(...);

    ........

};

}( jQuery ));


var htmlFactory = function(){

    //header
     this.header = function(object){
       console.log(object);
  }
 }
Smitt answered 25/2, 2016 at 3:39 Comment(0)
L
-1

What you did is basically extending jQuery.fn.messagePlugin object by new method. Which is useful but not in your case.

You have to do is using this technique

function methodA(args){ this // refers to object... }
function saySomething(message){ this.html(message);  to first function }

jQuery.fn.messagePlugin = function(opts) {
  if(opts=='methodA') methodA.call(this);
  if(opts=='saySomething') saySomething.call(this, arguments[0]); // arguments is an array of passed parameters
  return this.each(function(){
    alert(this);
  });

};

But you can accomplish what you want I mean there is a way to do $("#mydiv").messagePlugin().saySomething("hello"); My friend he started writing about lugins and how to extend them with your chainf of functionalities here is the link to his blog

Linseed answered 20/7, 2012 at 4:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.