Passing jQuery element data between methods? - javascript

I'm trying to create my first jQuery plugin, reading jQuery plugin authoring and one of the things it really emphasizes is to use methods instead of namespaces. I'm also trying to use the data from that guide to bounce variables between methods but it's not working. I need to create some data, pass it to another method to update it and send it somewhere else.
(function($) {
var methods = {
init : function(settings) {
return this.each(function(){
var x = 3;
var object = $(this);
// Bind variables to object
$.data(object, 'x', x);
$(object).bbslider('infoParse');
var y = $.data(object,'y');
alert(y);
}); // End object loop
}, // End init
infoParse : function() {
var object = $(this);
var x = $.data(object,'x');
alert(x);
var y = 10;
$.data(object,'y',y);
}, // End infoParse
}; // End method
$.fn.bbslider = function(method) {
if ( methods[method] ) {
return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + method + ' does not exist on jQuery.bbslider' );
}
}; // End slider
})(jQuery);
Here's a jsFiddle to show what I mean: http://jsfiddle.net/wRxsX/3/
How do I pass variables between methods?

see if this http://jsfiddle.net/wRxsX/6/ is ok ?
element.data(k,v)
$.data should use on element
http://api.jquery.com/data/
Store arbitrary data associated with the matched elements or return the value at the named data store for the first element in the set of matched elements.

Related

Extend jQuery Plugin with additional function / override function

I need to extend a jQuery Plugin (https://github.com/idiot/unslider) in order to add additional behavior with another public method.
(function(){
// Store a reference to the original remove method.
var originalMethod = $.fn.unslider;
// Define overriding method.
$.fn.unslider = function(){
// Execute the original method.
originalMethod.apply( this, arguments );
console.log( "Override method" );
function test() {
console.log("test called");
}
this.each(function() {
// Operations for each DOM element
console.log("each dom element?");
}).data('unslider', {
// Make test accessible from data instance
test: test
});
return this;
}
})(jQuery);
I already managed to make the public method accessible when calling
var slider = $('#slider');
slider.data('unslider').test();
However, I want to keep the old behavior of unslider anyways, but extend the Plugin with another function. Does anyone have an idea?
I created a fiddle, so you can check whats happening:
My new function gets called, but the old ones are gone:
http://jsfiddle.net/b2os4s7e/1/
If you look at the source of unslider, you can see it stores the Unslider instance inside the data:
// Enable multiple-slider support
return this.each(function(index) {
// Cache a copy of $(this), so it
var me = $(this),
key = 'unslider' + (len > 1 ? '-' + ++index : ''),
instance = (new Unslider).init(me, o);
// Invoke an Unslider instance
me.data(key, instance).data('key', key);
});
In your code you're overwriting this object with your own object. However, the slider expects there to be an Unslider instance. So what you want to do is get this instance and then extend it with your own functions:
var key = $(this).data('key');
var obj = $(this).data(key);
obj.test = function() { console.log('Working!'); };
See http://jsfiddle.net/b2os4s7e/2/
Just define:
$fn.unslider2 = function() { ... }
With any name and behaviour you like.
For extend JQuery should use .fn.extend
(function($){
$.fn.extend({
helloworld: function(message){
return this.each(function(){
$(this).click(function(){
alert(message);
});
});
}
});
})(jQuery)
the object .fn.extend is used for extend funcionality of jQuery
Thanks for your answers! I did it this way:
(function($){
var originalMethod = $.fn.unslider;
$.fn.extend({
unslider: function(o) {
var len = this.length;
var applyMethod = originalMethod.apply( this, arguments );
var key = applyMethod.data('key');
var instance = applyMethod.data(key);
// Cache a copy of $(this), so it
var me = $(this);
if (instance) {
instance.movenext = function (callback) {
return instance.stop().to(instance.i + 1, callback);
};
instance.moveprev = function (callback) {
return instance.stop().to(instance.i - 1, callback);
};
}
return applyMethod.data(key, instance);
}
});
})(jQuery)
The key was to address the data attribute as sroes suggested.
Moreover i needed to apply the original method, since i need the old methods.

Method for jQuery plugin

I'm trying to make a jQuery plugin for custom checkboxes and radio buttons.
(function($)
{
$.fn.checkboxRadio = function(options)
{
var defaults = some;
...
return this.each(function()
{
var button = $(this);
...
});
}
})(jQuery);
It can be used now by $('input').checkboxRadio(options);
How do I add a method check without changing current scope, to make a possible usage of something like $('input').checkboxRadio('check')?
How to handle a custom method and get its name inside my plugin?
Here is the official jquery plugin guide.
The part about wrapping functions is found here ("Plugin Methods") (the example is a would-be tooltip plugin) :
(function( $ ){
var methods = {
init : function(options) { ... },
show : function() { ... },
hide : function() { ... },
update : function(content) { ... }
};
$.fn.tooltip = function( method ) {
// Method calling logic
if ( methods[method] ) {
return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + method + ' does not exist on jQuery.tooltip' );
}
};
})(jQuery);
[update] explaining the methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 )) line in the guide :
If you call $(selector).tooltip('update', 'hello') from your javascript code, you want to end up calling the update method, passing 'hello' as the content argument, with this set to $(selector) for the duration of the call.
That is what this line takes care of :
if method == 'update', methods[method] is the update method,
arguments will be equal to ['update', 'hello'], you have to drop the first element to get the arguments you want to pass to your method ; this is exactly what Array.prototype.slice.call(arguments, 1) does,
myFunc.apply(obj, argsArray) calls the function myFunc, passing argsArray as the arguments, and setting this to obj for the duration of the call.
So inside your methods, you can call this.each(...) to iterate over all of the selector's items, e.g. :
update: function(content) {
this.each(function(){ $(this).data('tooltip.content', content); });
return this;
}
You can connect plugin methods like this:
(function($) {
$.fn.checkboxRadio = function(options) {
var defaults = {
check: 'check'
}
return this.each(function() {
var o = options;
var _this = $(this);
if( o.check === 'check' ) {
_this.attr('checked','checked');
}else if ( o.check === 'uncheck' ) {
_this.removeAttr('checked');
}
});
}
})(jQuery);
and user document should be like what you want: $('input').checkboxRadio({check:'check'});

Javascript call class w/o parenthesis (jQuery style)

I am trying to set up library like jQuery simply for learning purposes. I have it working decently well, and I can do method chaining as needed. The problem I am having is being able to call a method with the class' parenthesis:
What I have to do:
$foo().get('id1');
What I would like to do:
$foo.get('id1');
Here is the current javascript:
(function( window ) {
var document = window.document;
var fooTools = (function() {
var fooTools = function( selector ) {
return new fooTools.base.init( selector );
};
fooTools.base = fooTools.prototype = {
init: function( selector ) {
if( !selector ) {
return this;
}
if( selector ) {
this[0] = selector;
this.length = 1;
return this;
}
},
get: function( id ) {
return document.getElementById( id );
},
//..other methods
};
fooTools.base.init.prototype = fooTools.base;
return fooTools;
}());
window.fooTools = window.$foo = fooTools;
}( window ));
Currently it works just fine, but if I do not include the parenthesis I get an error that the .get() method does not exist. I still want to maintain the ability to use parenthesis for other methods, so i just want it be optional. Thanks!
change:
window.fooTools = window.$foo = fooTools;
to:
window.$foo = new fooTools();
window.fooTools = fooTools;
Live Example
EDIT
window.fooTools = fooTools;
window.$foo= fooTools;
window.$foo.get = fooTools.prototype.get;
Updated Example
Define two different kind of methods, just like we do in jQuery.
If the method needs parenthesis such as $foo('id1').attr({...}) define it using $foo.prototype.method = function, else, define it as a property of $foo $foo.get = function(){...}
Edit: This is actually based on IAbstract's answer.
It would be fooTools.prototype not $foo.prototype since fooTools is the class that $foo is an instance of.
Then you need to define $foo as something other than a function--right now you're returning fooTools, which is a function, so you need to call it in order to access what it exposes.
(Drat, he beat me to it.)

jQuery .data() and plugins

I am trying to use jQuery's .data() tool in a jQuery plugin but I'm getting some odd results.
Here is a cut down version of my plugin with the relevant code:
(function( $ ){
var methods = {
init : function( options ) {
// Do some stuff
this.data('name', 'New Component');
return this;
},
getStateData : function () {
// Function returns all data for save
var state = new Object;
state.name = this.data('name');
// snip ... Add some other bits to state
// ...
return state;
},
setStateData: function (args) {
var key = args[0];
var value = args[1];
// snip
this.data(key, value);
}
};
$.fn.component7Segment = function(method) {
if ( methods[method] ) {
return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + method + ' does not exist on jQuery.designComponent' );
}
};
})( jQuery );
If I call $(instance-of-plugin).component7Segment('getStateData'); the plugin returns the correct 'name' however if I call setStateData to change the name it has not changed the 'name' value when I next call getStateData.
Doing console.log(this); in each function looks slightly different so I started trying:
$.data($('#object-id'), 'name', value);
This still doesn't have the desired effect.
Can anybody see what I am missing?
Assuming you're calling setStateData like this:
$(instance).component7Segment("setStateData", "name", "Foo");
the setStateData function should be changed to look like this:
setStateData: function () {
var key = arguments[0];
var value = arguments[1];
// snip
this.data(key, value);
}
Note the removal of the args parameter and the use of the special arguments array-like object instead.
Example: http://jsfiddle.net/andrewwhitaker/ShFHC/
Your original problem was most likely that you were calling setStateData with parameters name and "foo". args[0] and args[1] was actually accessing the characters in name at that position instead of the arguments passed to the function.

Trying to learn jQuery plugin development

So I'm trying to learn how to implement method collection for a plugin based on this example: http://docs.jquery.com/Plugins/Authoring
What I cannot understand is how options that are extended with defaults for the plugin get sent to the individual methods.
I'm pretty sure any original options get sent to the method here:
return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
So how can you extend these arguments with defaults? The example doesn't really define how to do this...
var methods = {
init : function( options ) {
return this.each(function(){
$(window).bind('resize.tooltip', methods.reposition);
});
}
}
Also, here is the code from the example plugin authoring page:
(function( $ ){
var methods = {
init : function( options ) {
return this.each(function(){
$(window).bind('resize.tooltip', methods.reposition);
});
},
destroy : function( ) {
return this.each(function(){
$(window).unbind('.tooltip');
})
},
reposition : function( ) { // ... },
show : function( ) { // ... },
hide : function( ) { // ... },
update : function( content ) { // ...}
};
$.fn.tooltip = function( method ) {
if ( methods[method] ) {
return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof method === 'object' || ! method ) {
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + method + ' does not exist on jQuery.tooltip' );
}
};
})( jQuery );
Looks like my previous answer was closer to the mark than I previously thought.
Yes, this line is passing on the arguments:
return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
Using .apply(), you can call a method, change its context (this value), and give it a collection of arguments instead of individual ones. Handy if you don't know how may arguments need to be passed on.
So you can break the above line down like this:
// reference the arguments passed to the plugin
var args = arguments;
// eliminate the first one, since we know that's just the name of the method
args = Array.prototype.slice.call( arguments, 1 )
// call the method that was passed,
// passing along the Array of the arguments (minus the first one),
return methods[method].apply( this, args);
// Note that it is being called from the context of "this" which is the jQuery object
So if the plugin was called like this:
$('.someElem').tooltip('destroy', 'someArg', 'anotherArg' );
The code will locate the "destroy" method, slice "destroy" off of the Arguments object, and call "destroy" passing along the Array of remaining arguments.
you use $.extend(existingObject || {} (new object), oneObject, secondObject, nthObject)
var mydefaults = { myDefaultArg : "foo" }
var inputOptions = { myDefaultArg : "Somethin else" }
var options = $.extend({}, mydefaults, inputOptions);
options.myDefaultArg == "Somethin else";
To access data, or to save them,you can use the data method
so if you are in the plugin, "this" is the jquery element element, you can now save data into it.
$(this).data("pluginname.somedataname", data);
and retriev it
var data = $(this).data("pluginname.somedataname");

Categories