2009-10-07 7 views
5

Estoy tratando de crear un complemento jQuery que creará algo así como un autoCompleteBox pero con características personalizadas. ¿Cómo guardo las variables miembro para cada elemento jQuery coincidente?¿Cómo tener variables miembro y métodos públicos en un plugin jQuery?

Por ejemplo, tendré que almacenar un ID de tiempo para cada uno. También me gustaría almacenar referencias a algunos de los elementos DOM que componen el control.

Me gustaría ser capaz de hacer un método público que funciona algo así como:

$("#myCtrl").autoCompleteEx.addItem("1"); 

Pero en la implementación de addItem() ¿Cómo puedo acceder a las variables miembro de ese objeto particular, al igual que su timerId ¿o lo que sea?

A continuación se muestra lo que tengo hasta ahora ...

Gracias por cualquier ayuda o sugerencias!

(function($) 
{  
    //Attach this new method to jQuery 
    $.fn.autoCompleteEx = function(options) 
    {  
     //Merge Given Options W/ Defaults, But Don't Alter Either 
     var opts = $.extend({}, $.fn.autoCompleteEx.defaults, options); 


     //Iterate over the current set of matched elements 
     return this.each(function() 
     { 
      var acx = $(this); //Get JQuery Version Of Element (Should Be Div) 

      //Give Div Correct Class & Add <ul> w/ input item to it 
      acx.addClass("autoCompleteEx"); 
      acx.html("<ul><li class=\"input\"><input type=\"text\"/></li></ul>"); 

      //Grab Input As JQ Object 
      var input = $("input", acx); 

      //Wireup Div 
      acx.click(function() 
      { 
       input.focus().val(input.val()); 
      }); 


      //Wireup Input 
      input.keydown(function(e) 
      { 
       var kc = e.keyCode; 
       if(kc == 13) //Enter 
       { 

       } 
       else if(kc == 27) //Esc 
       { 

       } 
       else 
       { 
        //Resize TextArea To Input 
        var width = 50 + (_txtArea.val().length*10); 
        _txtArea.css("width", width+"px");  
       } 
      }); 

     }); //End Each JQ Element 

    }; //End autoCompleteEx() 

    //Private Functions 
    function junk() 
    { 

    }; 

    //Public Functions 
    $.fn.autoCompleteEx.addItem = function(id,txt) 
    { 
     var x = this; 
     var y = 0; 
    }; 

    //Default Settings 
    $.fn.autoCompleteEx.defaults = 
    { 
     minChars: 2, 
     delay:  300, 
     maxItems: 1 
    }; 

    //End Of Closure 
})(jQuery); 

Respuesta

7

He descubierto que la forma en jQuery UI de manejar esto parece funcionar lo mejor. Usted crea sus 'métodos adicionales' como una cadena de argumentos para el plugin:

$('#elem').autoCompleteEx('addItem', '1'); 

A continuación, se conserva el 'este' contexto, y se puede hacer algo en este sentido: la manipulación

function addItem() { 
    // this should be == jquery object when this get called 
} 

$.fn.autoCompleteEx = function(options) { 
    if (options === 'addItem') { 
    return addItem.apply(this, Array.prototype.splice.call(arguments, 1)); 
    } 
}; 
+0

Eres un genio ... ¡Gracias a un millón por esto! – Legend

1

Eche un vistazo a la funcionalidad .data de jQuery. Te permite almacenar pares clave/valor en cualquier objeto.

+0

Sí, que parece que puede funcionar para mí. ¿Sabes si ese es el método preferido para lo que estoy tratando de lograr? – user169867

+0

Una desventaja es que los datos no son privados. – user169867

1

usar algo como esto:

acx.data("acx-somename", datavalue); 

A continuación, se puede recuperar posteriormente con:

var datavalue = acx.data("acx-somename"); 
+0

Dado que los datos son compartidos por todo lo que almacena datos relacionados con el objeto DOM en cuestión, el prefijo "acx-" está allí como un espacio de nombres. –

+0

Ya veo. Gracias Anthony y Corey. – user169867

4

Aquí es una plantilla que estoy experimentando con la hora de construir los plugins de widgets más complejos:

(function($){ 

    // configuration, private helper functions and variables 
    var _defaultConfig = { 
     /* ... config ... */ 
     }, 
     _version = 1; 


    // the main controller constructor 
    $.myplugin = function (elm, config) { 

    // if this contructor wasn't newed, then new it... 
    if (this === window) { return new $.myplugin(elm, config || {}); } 

    // store the basics 
    this.item = $(elm); 
    this.config = new $.myplugin.config(config); 

    // don't rerun the plugin if it is already present 
    if (this.item.data('myplugin')) { return; } 

    // register this controlset with the element 
    this.item.data('myplugin', this); 

    // ... more init ... 

    }; 
    $.myplugin.version = _version; 
    $.myplugin.config = function (c) { $.extend(this, $.myplugin.config, c); }; 
    $.myplugin.config.prototype = _defaultConfig; 
    $.myplugin.prototype = { 

    /* ... "public" methods ... */ 

    }; 

    // expose as a selector plugin 
    $.fn.myplugin = function (config) { 
    return this.each(function(){ 
     new $.myplugin(this, config); 
    }); 
    }; 

})(jQuery); 

pongo la configuración y la versión predeterminadas en la parte superior simplemente porque es la más probable Lo que busca cualquier persona que lea el código. La mayoría de las veces solo quiere examinar el bloque de configuraciones.

Esto expondrá "miplugin" en dos lugares, como un constructor para "controlador" del widget en $ y como un método de recogida en $.fn. Como puede ver, el método $.fn realmente no hace nada excepto crear nuevos controladores.

La configuración es un objeto heredado prototípicamente donde el prototipo es el predeterminado. Esto le da flexibilidad extendida con los valores de Asignar como usted puede asignar los "próximos" valores predeterminados en $.myplugin.config, o alterar el valor predeterminado de cada complemento en ejecución con $.myplugin.config.prototype. Esto requiere que siempre se asigne a estos con $ .extend o romperá el sistema. Más código podría contrarrestar eso, pero prefiero saber lo que estoy haciendo. :-)

La instancia del controlador se une al elemento a través del método jQuery data(), y de hecho lo usa para probar que no se ejecuta dos veces en el mismo elemento (aunque es posible que desee permitir su reconfiguración)

Esto le da la siguiente interfaz al controlador:

// init: 
$('div#myid').myplugin(); 

// call extraMethod on the controller: 
$('div#myid').data('myplugin').extraMethod(); 

El mayor defecto de este enfoque es que es un poco de dolor para mantener el "este" contexto con cada asignación evento.Hasta que el contexto de los eventos llegue a jQuery, esto debe hacerse con una cantidad generosa de cierres.

Aquí está un ejemplo general de cómo podría ser un plugin (incompleta e inútil):

(function($){ 

    // configuration, private helper functions and variables 
    var _defaultConfig = { 
     openOnHover: true, 
     closeButton: '<a href="#">Close</a>', 
     popup: '<div class="wrapper"></div>' 
     }, 
     _version = 1; 

    // the main controller constructor 
    $.myplugin = function (elm, config) { 

    // if this contructor wasn't newed, then new it... 
    if (this === window) { return new $.myplugin(elm, config || {}); } 
    this.item = $(elm); 
    this.config = new $.myplugin.config(config); 
    if (this.item.data('myplugin')) { return; } 
    this.item.data('myplugin', this); 

    // register some events 
    var ev = 'click' + (this.config.openOnHover) ? ' hover' : ''; 
    this.item.bind(ev, function (e) { 
     $(this).data('myplugin').openPopup(); 
    }); 

    }; 
    $.myplugin.version = _version; 
    $.myplugin.config = function (c) { $.extend(this, $.myplugin.config, c); }; 
    $.myplugin.config.prototype = _defaultConfig; 
    $.myplugin.prototype = { 

    openPopup: function() { 
     var C = this.config; 
     this.pop = $(C.popup).insertAfter(this.item); 
     this.pop.text('This says nothing'); 
     var self = this; 
     $(C.closeButton) 
      .appendTo(pop) 
      .bind('click', function() { 
      self.closePopup(); // closure keeps context 
      return false; 
      }); 
     return this; // chaining 
    }, 

    closePopup: function() { 
     this.pop.remove(); 
     this.pop = null; 
     return this; // chaining 
    } 

    }; 

    // expose as a selector plugin 
    $.fn.myplugin = function (config) { 
    return this.each(function(){ 
     new $.myplugin(this, config); 
    }); 
    }; 

})(jQuery); 
0

Instancia ! Eso es lo que quieres, ¿verdad? Buena vieja moda, manipulación de instancias en tiempo real. Eso es lo que yo también quería. Busqué en Google la misma pregunta y no pude obtener una buena respuesta en ninguna parte (como las de arriba) así que me di cuenta. No me gusta mi solución porque parece una ronda sobre la forma de acceder a los métodos de instancia y extraña para un consumidor de jquery, pero jQuery es raro en primer lugar, pero encantador. Escribí un simple plugin a desvanecerse imágenes, pero luego una vez que tenía que hacer más con ella, quería exponer métodos para una instancia en vivo para conseguir este resultado aquí ->example, hice lo siguiente:

var instanceAccessor = {}; 
var pluginOptions = {'accessor':instanceAccessor} 
$('div').myPlugin(pluginOptions); 

Entonces Dentro del plugin, agrego métodos a esto pasado en el objeto 'accessor' dado que es un objeto. Expongo los métodos dentro del plug-in de esta manera:

if (pluginOptions.accessor != null && typeof(pluginOptions.accessor) === 'object') { 
    pluginOptions.accessor.exposedMethod = function (someParam) { 
    // call some private function here and access private data here 
    }; 
} 

Entonces, el consumidor de este plugin se puede llamar al método o métodos instancia cualquier momento durante el tiempo de ejecución como solíamos hacer antes de jQuery hecho esta extraña:

instanceAccessor.exposedMethod('somevalue'); 

puede buscar "fundido encadenado mudo" en la búsqueda plugin de jQuery para encontrar mi complemento mudo y ver el código por sí mismo.

1

Aquí está mi opinión sobre ella:

Tengo un objeto dentro del cierre que se utiliza para crear objetos de instancia. Los objetos de instancia se adjuntan al nodo del elemento utilizando el método de datos de jQuery(). Estos objetos de instancia tienen métodos públicos a los que puede llamar según sea necesario.

(function($) 
{  
// This is used to create AutoComplete object that are attatched to each element that is matched 
// when the plugin is invoked 
var AutoCompleteEx = function(options, acx) { 

    // PRIVATE VARIABLES 
    var timerID; 
    var input; 

    //Give Div Correct Class & Add <ul> w/ input item to it 
    acx.addClass("autoCompleteEx"); 
    acx.html("<ul><li class=\"input\"><input type=\"text\"/></li></ul>"); 

    //Grab Input As JQ Object 
    input = $("input", acx); 

    //Wireup Div 
    acx.click(function() 
    { 
     input.focus().val(input.val()); 
    }); 


    //Wireup Input 
    input.keydown(function(e) 
    { 
     var kc = e.keyCode; 
     if(kc == 13) //Enter 
     { 

     } 
     else if(kc == 27) //Esc 
     { 

     } 
     else 
     { 
      //Resize TextArea To Input 
      var width = 50 + (_txtArea.val().length*10); 
      _txtArea.css("width", width+"px");  
     } 
    }); 

    // PUBLIC METHODS 

    this.setTimerID = function(id) { 
    timerID = id; 
    }; 

    this.getTimerID = function() { 
    return timerID; 
    }; 

}; 


//Attach this new method to jQuery 
$.fn.autoCompleteEx = function(options) 
{  
    //Merge Given Options W/ Defaults, But Don't Alter Either 
    var opts = $.extend({}, $.fn.autoCompleteEx.defaults, options); 

    //Iterate over the current set of matched elements 
    return this.each(function() 
    { 
     var acx = $(this); //Get JQuery Version Of Element (Should Be Div) 

     // creating a new AutoCompleteEx object and attach to the element's data, if not already attached 
     if (!acx.data('autoCompleteEx')) { 
      acx.data('autoCompleteEx', new AutoCompleteEx(options, acx)); 
     } 

    }); //End Each JQ Element 

}; //End autoCompleteEx() 

//Default Settings 
$.fn.autoCompleteEx.defaults = 
{ 
    minChars: 2, 
    delay:  300, 
    maxItems: 1 
}; 

//End Of Closure 
})(jQuery); 

Puede llamar a los métodos como éste:

$("div#someDiv").autoCompleteEx(); 
$("div#someDiv").data('autoCompleteEx').setTimerID(123); 
var timerId = $("div").data('autoCompleteEx').getTimerID(); 
console.log(timerId); // outputs '123' 

Y si está instanciar más de uno:

$("div.someDiv").autoCompleteEx(); 
$("div.someDiv").eq(0).data('autoCompleteEx').setTimerID(123); 
$("div.someDiv").eq(1).data('autoCompleteEx').setTimerID(124); 
var firstTimerId = $("div").eq(0).data('autoCompleteEx').getTimerID(); 
var secondTimerId = $("div").eq(1).data('autoCompleteEx').getTimerID(); 
console.log(firstTimerId); // outputs '123' 
console.log(secondTimerId); // outputs '124' 
Cuestiones relacionadas