I'm trying to create a jQuery plugin that will create something like an autoCompleteBox but with custom features. How do I store member variables for each matching jQuery element?
For example I'll need to store a timerID for each. I'd also like to store references to some of the DOM elements that make up the control.
I'd like to be able to make a public method that works something like:
$("#myCtrl").autoCompleteEx.addItem("1");
But in the implementation of addItem() how can I access the member variables for that particular object like its timerID or whatever?
Below is what I have so far...
Thanks for any help or suggestions!
(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);
I've found that the jQuery UI way of handling this seems to work out the best. You create your 'extra methods' as a string argument to your plugin:
$('#elem').autoCompleteEx('addItem', '1');
Then the 'this' context is preserved, and you can do something along these lines:
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));
}
};
Here is a template that I am experimenting with when building more complex widget plugins:
(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);
I put the default config and version at the top simply because it the most likely thing anyone reading the code is seeking. Most of the time you just want to examine the settings block.
This will expose "myplugin" in two places, as a constructor for the widget's "controller"
on $
and as a collection method on $.fn
. As you can see $.fn
method doesn't really do anything except instanciate new controllers.
The config is a prototypally inherited object where the default is the prototype. This give extended flexibility with asigining values as you may
assign the "next" defaults into $.myplugin.config
, or alter every running plugin's default with $.myplugin.config.prototype
. This does require you to
allways assign into these with $.extend or you will break the system. More code could counter that, but I prefer to know what I'm doing. :-)
The instance of the controller is binds itself to the element through jQuery's data()
method, and in fact uses it to test that it isn't run twice on the same element (although you might want to allow reconfiguring it).
This gives you the following interface to the controller:
// init:
$( 'div#myid' ).myplugin();
// call extraMethod on the controller:
$( 'div#myid' ).data('myplugin').extraMethod();
The biggest flaw on this approach is that it is a bit of a pain to maintain the "this" context with every event assignment. Until context for events arrives in jQuery this needs to be done with a liberal amount of closures.
Here is a rough example of how an (incomplete and useless) plugin might look:
(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);
Take a look at the .data functionality of jQuery. It lets you store key/value pairs on any object.
Use something like this:
acx.data("acx-somename", datavalue);
Then you can later retrieve it with:
var datavalue = acx.data("acx-somename");
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With