Soy nuevo en la creación de complementos jQuery, pero tengo bastante experiencia en su uso. Algunos complementos con los que he trabajado solo se pueden inicializar mediante programación, es decir

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

Encuentro esto molesto, pero he logrado descubrir cómo escribir un complemento básico como este a partir de tutoriales y mirando el código de otros complementos.

Otros, como el componente modal de Bootstrap, se pueden inicializar únicamente a través del marcado, es decir,

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

Y luego, simplemente al incluir el archivo .js del complemento, cualquier entrada con la clase fancy-pants se inicializa automáticamente como un complemento fancyPants. Me gusta este comportamiento, aunque sé que acapara un nombre de clase en particular.

Pregunta 1: ¿cómo hacen esto? Miré a través de bootstrap.js, pero parece que hay muchas cosas que no entiendo.

Pregunta 2: Digamos que tengo un complemento que abarca múltiples elementos en el DOM. Por ejemplo,

<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>

Sin cambiar la estructura de marcado (por ejemplo, agregar un contenedor contenedor), ¿cómo puedo inicializar automáticamente dos instancias de mi complemento, una que cubra todos los botones con una clase de bootstrapradio y nombre {{X1 }}, y otro que cubre todos los botones con una clase de bootstrapradio y nombre beverage?

EDITAR: Esto es con lo que estoy trabajando en este momento.

(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 ));

Un ejemplo de cómo me gustaría que este complemento transformara el DOM:

ANTES de 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>

DESPUÉS 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">
4
alexw 31 ago. 2014 a las 23:25

4 respuestas

La mejor respuesta

Bueno, la respuesta de @ ArtOfCode me puso en el camino correcto. Básicamente, terminé usando el patrón .each dentro de $(document).ready, y luego verifico si el campo oculto input se ha agregado para el grupo con nombre al que pertenece el elemento. De esa manera, puedo agregar las clases btn btn-xs a todos los elementos, pero solo agrego la entrada oculta una vez.

Luego, conservé mi código original con el método init para permitir la inicialización del complemento mediante programación.

Esto es lo que se me ocurrió, lo estoy lanzando bajo la licencia MIT :)

https://github.com/alexweissman/bootstrapradio

0
alexw 31 ago. 2014 a las 22:50

Puede seleccionar atributos con jQuery de esta manera

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

Entonces, en tu ejemplo tendrías algo como

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

Muchos complementos hacen esto sin la clase, al tener un nombre de atributo específico del complemento.

Un ejemplo es Lightbox2, que encuentra todos sus elementos posibles usando $("[data-lightbox]"), y luego los separa ellos en grupos de lightbox individuales basados en el valor de esos atributos.

Si desea que lo haga automáticamente, simplemente envolvería esta misma funcionalidad en un documento listo dentro de su archivo de complemento.

Como ejemplo, para este HTML:

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

Y esta 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
    }

});

Terminas con 2 bootstrapradios, el primero que se inicializó con dos elementos, y el segundo que se inicializó con uno.

3
iabw 31 ago. 2014 a las 20:09

Bueno, depende de lo que quieras hacer en tu inicialización. En general, supongo que tendrá que usar una cláusula preparada para documentos con alguna lógica de comprobación de atributos y un código de inicialización personalizado.

Aquí está la construcción estándar para los complementos jQuery:

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

Creo que tendrá que modificar eso para que cuando esté listo para documentos, su complemento se inicialice, y luego, si aún desea poder llamarlo, también agrega la función:

(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);

Si desea utilizar su método existente init y simplemente darle el nombre, es aún más fácil:

(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);

El self es importante para darle acceso al alcance correcto aquí.

Además, su código actual tiene un error de sintaxis. Tu ultima linea:

}( jQuery ));

Debe ser:

})(jQuery);

EDITAR: Vale la pena señalar que ese último punto no es realmente un error de sintaxis después de todo, de acuerdo con el artículo vinculado en el comentario a continuación. Cualquiera de los dos funciona.

1
ArtOfCode 31 ago. 2014 a las 20:08

Prueba (este patrón)

// 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
guest271314 31 ago. 2014 a las 21:11