Public functions from within a jQuery plugin
Asked Answered
D

5

34

I love the jQuery plugin architecture, however, I find it frustrating (probably due to a lack of understanding on my part) when I want to retain a reference to the plugin instance to access properties or methods later on in my code.

Edit: I want to clarify that what I am really trying to do is retain a reference to the methods and properties used within the plugin, so that I can use them later

Lets take the case of a AJAX loading icon. In a more traditional OOP environment, I could do:

var myIcon = new AJAXIcon();
myIcon.start();
//some stuff
myIcon.stop();

The methods and properties of my object are stored on a variable for later use. Now if I want to have the same functionality in a jQuery plugin, I would call it from my main code somewhat like this:

$("#myId").ajaxIcon()

By convention, my plugin needs to return the original jQuery object passed to my plugin allowing for chainability, but if I do that, I loose the ability to access methods and properties of the plugin instance.

Now, I know that you can declare a public function in my plugin, somewhat along the lines of

$.fn.ajaxIcon = function(options) {
    return this.each(function () {
        //do some stuff
    }
}

$.fn.ajaxIcon.stop = function() {
    //stop stuff
}

However, without breaking the convention of returning the original jQuery object, I can't retain a reference to the specific instance of the plugin that I want to refer to.

I would like to be able to do something like this:

var myIcon = $("myId").ajaxIcon(); //myIcon = a reference to the ajaxIcon 
myIcon.start();
//some stuff
myIcon.stop();

Any thoughts?

Dumbbell answered 23/6, 2009 at 18:29 Comment(0)
B
94

If you do something like the following:

(function($){

$.fn.myPlugin = function(options) {
    // support multiple elements
    if (this.length > 1){
        this.each(function() { $(this).myPlugin(options) });
        return this;
    }

    // private variables
    var pOne = '';
    var pTwo = '';
    // ...

    // private methods
    var foo = function() {
        // do something ...
    }
    // ...

    // public methods        
    this.initialize = function() {
        // do something ...
        return this;
    };

    this.bar = function() {
        // do something ...
    };
    return this.initialize();
}
})(jQuery);

Then you can access any of your public methods:

var myPlugin = $('#id').myPlugin();

myPlugin.bar();

This is taken from this very helpful article (May 2009) from trulyevil.com which is itself an extension on this article (Oct 2007) from learningjquery.com.

Bashemath answered 25/6, 2009 at 12:38 Comment(5)
This is an excellent method, even beyond what you explain here... as I can do something like var myPlugin = $('id').myPlugin().otherFunction().hide(), and myPlugin will still have the correct functions, as they are added to the DOM node that is passed into the jQuery function.Dumbbell
After a few days if trying to figure this out thank god this is here.. Is there any development on this since 2009?Domitiladomonic
I like this solution in that it loops through and applies itself to each element, but I find there is a flaw with the public methods. When there is only one element, you can easily call the method directly on the element reference. However, if there is more than 1, the methods are applied to each individual and the reference to all of them. So, you can still call the method on the reference to the elements, but how do you call the method on an individual element? I've tried $elem.eq(0).foo() and $elem[0].foo(), but I get "undefined is not a function". This may be a flaw with jQuery in general.Vizor
how to access/call the private function inside the plugin. I tried this.foo() and just foo(), but it dint work :(Periosteum
@Bashemath Thank you for the best explanation of jQuery plug-in construction, I have ever come across. Very concise and clear. Excellent answer...Frigate
D
3

Ok, i figured out how to do this:

Plugin Code:

$.ajaxIcon.init = function(element, options) {
    //your initialization code

    this.start = function() {
         //start code
    }

    this.stop = function() {
         //stop code
    }
}

$.fn.ajaxIcon = function(options) {
    this.each(function () {
        //This is where the magic happens
        jQuery(this).data('ajaxIcon', new jQuery.ajaxIcon.init(this, opts));
    });

return this;
}

Then to use it somewhere else in your code:

var myIcon = $("#myId").ajaxIcon.data('ajaxIcon') 
// myIcon: a reference to the 'init' object specific to this plugin instance
myIcon.start();
myIcon.stop();

voila, answered my own question :)

Dumbbell answered 23/6, 2009 at 19:33 Comment(3)
Did you find this as example somewhere else, as I'd like to know a bit more about the data bits - it seems silly that the caller needs to know about that when creating an instance.Bashemath
Actually on further research you don't have to mess around with all this, see my answer.Bashemath
DEfusion's method is much better... i wouldn't use this method unless you don't want to attach the functions directly into the DOM for some reason.Dumbbell
C
2

Sample plugin with all functions:

    <script type="text/javascript">
(function( $ ) {

    $.fn.PluginX = function( options ) {
        // Default variables
        // var defaults = {textColor: "#000", backgroundColor: "#fff"}
        // var opts = $.extend( {}, defaults, options );

        // Or
        var settings = $.extend( {}, $.fn.PluginX.defaults, options );

        // Private function
        var privFunc = function(txt) {
            return "Private function " + txt;
        };

        // Public function
        this.pubFunc = function(txt) {
            console.log("\r\nPublic function " + txt);
            return this;
        };

        // Public function2
        this.pubFunc2 = function(txt) {
            console.log("\r\nPublic function " + txt);

            // Private from public
            privFunc("\r\nPrivate function from public -- " + txt);
            return this;
        };

        // Our plugin implementation code goes here.
        return this.each(function() {
            // alert(opts.validate);
            $(this).on('click',function(){
                var elem = $( this );
                var markup = elem.text();                                
                console.log("\r\nElement text " + markup);

                // Function private
                console.log(privFunc(markup));

                // External func
                console.log($.fn.PluginX.format(markup));

                // External function
                console.log(external(markup));
            });

            return this;
        });
    };

    // Variable
    $.fn.PluginX.defaults = {
        validate: "username"
    };

    // External Function
    $.fn.PluginX.format = function(txt) {
        return "<strong>" + txt + "</strong>";
    };

    // External Function
    function external(txt){
        return "External " + txt;
    }

})(jQuery);

$(document).ready(function() {
    var pl = $('.txt').PluginX({validate: "words"}).pubFunc("Hello Public").pubFunc2("Hello Public2");        
});
</script>
<p class="txt">Hello Max</p>
<p class="txt">Hello Pax</p>
Canea answered 26/7, 2019 at 15:58 Comment(2)
Add some explanation with answer for how this answer help OP in fixing current issueWag
This is absolutely the best answer by far. Thank you for the complete commented example.Soft
M
0

I think you could accomplish what you're looking for with something like this:

var myIcon = $("myId").ajaxIcon(); //myIcon = a reference to the ajaxIcon 
$.ajaxIcon.start(myIcon);//some stuff
$.ajaxIcon.stop(myIcon);

Haven't tested it though - I don't have access to an environment where I can do that atm

Marrowfat answered 23/6, 2009 at 19:7 Comment(0)
O
-1

Most of the jQuery plugins that I see trying to accomplish this will use an anonymous scope and closures to reference functions and variables unique to that instance. For example, using the following pattern:

;(function ($) {
    // your code
})(jQuery);

Between the beginning and end of the module, you can declare any functions you want. You won't pollute the global namespace and you can retain access to local variables through closures, that could solve a lot of your problems. Also, don't be afraid to use the $.data functions.

Opening answered 23/6, 2009 at 18:37 Comment(1)
hmm.... i think my questions isn't clear enough. I get how to make a jQuery plugin, with the closures and whatnot, but what I really want to know is how to access existing instances of the plugin to access properties and trigger methods.Dumbbell

© 2022 - 2024 — McMap. All rights reserved.