Create a jQuery plugin that initializes itself based on HTML class and name

I'm new to creating jQuery plugins, but I have good experience using them. Some plugins I've worked with can be initialized programmatically, i.e.

$("input[name='blah']").fancyPants();

      

I find it annoying, but I managed to figure out how to write a basic plugin like this from the tutorials and look at the code of other plugins.

Others, like the Bootstrap load module, can only be initialized via markup, i.e.

<input type='text' name='blah' class='fancy-pants'>

      

And then by simply including the plugin .js file, any input with the class is fancy-pants

automatically initialized as a plugin fancyPants

. I like this behavior, although I know that it spawns a specific class name.

Question 1: how do they do it? I've looked bootstrap.js

but there seems to be a lot that I don't understand.

Question 2: Let's say I have a plugin that includes multiple elements in the DOM. For example,

<button class="bootstrapradio" name="primary_group" value="epicurist"><i class='fa fa-cutlery'></i></button>
<button class="bootstrapradio" name="primary_group" value="futurist"><i class='fa fa-space-shuttle'></i></button>
<button class="bootstrapradio" name="primary_group" value="stoic"><i class='fa fa-tree'></i></button>        

<button class="bootstrapradio" name="beverage" value="beer"><i class='fa fa-beer'></i></button>
<button class="bootstrapradio" name="beverage" value="martini"><i class='fa fa-glass'></i></button>

      

Without changing the markup structure (like adding a wrapper container), how can I automatically initialize two instances of my plugin that covers all buttons with class bootstrapradio

and name primary_group

, and another that covers all buttons with class bootstrapradio

and name beverage

?

EDIT: This is where I am currently working.

(function ( $ ) {

    var methods = {
        init : function(options) {
            // Initialization method, adds classes to elements and binds events
            ...
            return this;
        },
        value : function( new_val ) {
            // Method to get/set value
            ...
        }
    };

    $.fn.bootstrapradio = 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.bootstrapradio' );
        }    
    };    
}( jQuery ));

      

An example of how I would like this plugin to transform the DOM:

BEFORE document.ready

:

    <button class="bootstrapradio" name="primary_group" value="epicurist" title='Epicurist' disabled><i class='fa fa-cutlery'></i></button>
    <button class="bootstrapradio" name="primary_group" value="futurist" title='Futurist'><i class='fa fa-space-shuttle'></i></button>
    <button class="bootstrapradio" name="primary_group" value="stoic" title='Stoic'><i class='fa fa-tree'></i></button>        

    <button class="bootstrapradio" name="beverage" value="beer" title='Beer'><i class='fa fa-beer'></i></button>
    <button class="bootstrapradio" name="beverage" value="martini" title='Martini'><i class='fa fa-glass'></i></button>

      

AFTER document.ready

:

    <button class="btn btn-xs bootstrapradio" name="primary_group" value="epicurist" title='Epicurist' disabled><i class='fa fa-cutlery'></i></button>
    <button class="btn btn-xs bootstrapradio" name="primary_group" value="futurist" title='Futurist'><i class='fa fa-space-shuttle'></i></button>
    <button class="btn btn-xs bootstrapradio" name="primary_group" value="stoic" title='Stoic'><i class='fa fa-tree'></i></button>
    <input type="hidden" name="primary_group">

    <button class="btn btn-xs bootstrapradio" name="beverage" value="beer" title='Beer'><i class='fa fa-beer'></i></button>
    <button class="btn btn-xs bootstrapradio" name="beverage" value="martini" title='Martini'><i class='fa fa-glass'></i></button>
    <input type="hidden" name="beverage">

      

+3


source to share


4 answers


Well, @ArtOfCode's answer put me on the right track. Basically, I ended up using the template .each

internally $(document).ready

and then checked if a hidden field had been added input

for the named group that this element belongs to. This way I can add classes btn btn-xs

to all elements, but only add the hidden input once.

Then I saved the original code with a method init

to programmatically initialize the plugin.



Here is what I came up with, I am releasing it under MIT license :)

https://github.com/alexweissman/bootstrapradio

0


source


You can select attributes with jQuery this way

$("[data-role='modal']");

      

So in your example, you would have something like

$(".bootstrapradio[name='primary_group']").myPlugin();
$(".bootstrapradio[name='beverage']").myPlugin();

      

Many plugins do this without a class, having the name of the plugin attribute.

One example is Lightbox2 , which finds all possible elements using $("[data-lightbox]")

and then divides them into separate lightbox groups based on the value of these attributes.



If you wanted this to be done automatically, you would simply wrap the same functionality in a document ready in your plugin file.

As an example, for this HTML:

<div data-bootstrapradio='type1'></div>
<div data-bootstrapradio='type1'></div>
<div data-bootstrapradio='type2'></div>

      

And this Javascript:

$(function(){

    $.fn.bootstrapradio = function(params) {
        return $(this).each(function() {
            // Your code
        });
    }

    // Get all the radios on the page currently
    var $radios = $("[data-bootstrapradio]");

    // Get the groupings of them
    var radioTypes = [];
    $radios.each(function(i,el){
        var type = $(this).attr("data-bootstrapradio");
        if (radioTypes.indexOf(type) === -1){
            radioTypes.push(type);
        }
    });

    // Initialize the plugin using all radios of each type
    for (var i=0; i<radioTypes.length; i++){

        var type = radioTypes[i];

        var radioGroup = $radios.filter(function(i,el){
            return $(el).is("[data-bootstrapradio="+type+"]");
        }).bootstrapradio();

        // Do whatever else with radioGroup you want for initialization
    }

});

      

As a result, you will get 2 bootstrapradios, the first of which was initialized with two elements, and the second one was initialized with one.

+2


source


Well, it depends on what you want to do in your initialization. In general, my guess is that you have to use a document clause with some attribute validation logic and custom initialization code.

Here's the default construct for jQuery plugins:

(function($) {
    $.fn.myPlugin = function(params) {
         return $(this).each(function() {
             // do your plugin stuff
         });
    }
})(jQuery);

      

I think you will have to change this so that in the finished document your plugin initializes and then perhaps if you still want to call it you add this function as well:

(function($) {
    $(document).ready(function() {
        $(".bootstrapradio").each(function() {
            if($(this).attr("name") == "primary-group") {
                // do something specific for name 'primary-group'
            }
            else if($(this).attr("name") == "something-else") {
                // do something else
            }
            else {
                // either catch-all or just drop the element
            }
        });
    });

    $.fn.myPlugin = function(params) {
        // do your plugin stuff
    }
})(jQuery);

      

If you want to use an existing method init

and just specify its name, it's even easier:

(function($) {
    var self = this;

    $(document).ready(function() {
        $(".bootstrapradio").each(function() {
            self.methods.init($(this).attr("name"));
        });
    });

    $.fn.myPlugin = function(params) {
        // do your plugin stuff
    }
})(jQuery);

      

self

it is important to give you access to the correct area.

Also, your current code has a syntax error. Your last line:

}( jQuery ));

      

it should be:

})(jQuery);

      

EDIT: It's worth noting that the last point is not actually a syntax error, as per the article cited in the comment below. Either it works.

+1


source


Try (this pattern)

// hold jquery `ready` event until `plugin` "ready"
$.holdReady(true);
$(function() {
  // `deferred` until _after_ `plugin` defined ,
  // `ready` `hold` `resolved` within `plugin`
  $.bootstrapradio().css("font-size", "24px")
});

// `plugin` 
(function($, window, undefined) {
    // define `plugin` as `function` and `object`
    // check document for `bootstrapradio` `class` elements ,
    // if true , proceed , if not ,
    // create empty jquery object , proceed
    // define logic checks for elements , objects , other
    // in window , document , other
    $.fn.bootstrapradio = $.bootstrapradio = function(options) {
        $elems = $(".bootstrapradio").is("*") ? $(".bootstrapradio") : $({});
        // settings
        $.bootstrapradio.settings = {
            "primary_group" : "green"
            , "beverage" : "blue"
        };
        // extend settings to options
        var options = $.extend($.bootstrapradio.settings, options);
        // do stuff
        // e.g., apply `settings`/`options` to `$elem`
        // based on `name` , `value` attributes in `html`
        $elems.each(function(k, v) {
            $.each(options, function(_name, _value) {
                if (_name === v.name) {
                  $(v).html(v.value).css("color", _value)
                }
            })
        });
        // return `$elems`
        return $elems;
    } || {};
    // call `plugin`
    $.when($.bootstrapradio())
    .done(function(_$elem) {
        // check if `plugin` type cast
        // to both `function` and `object` ,
        // redundant check if `plugin` `in` window
        if (typeof $.fn.bootstrapradio === "function" 
           && typeof $.bootstrapradio === "object"
           && $.bootstrapradio in window) {
               // resolve jquery `ready` (`document.ready`) `hold` ,
               // do other stuff ,
               // e.g., `$.bootstrapradio().css("font-size", "24px")`
               $.holdReady(false);
        };
    })

}(jQuery, window));

      

jsfiddle http://jsfiddle.net/guest271314/f2asu701/

0


source







All Articles