jQuery widget function not getting called - javascript

Update:
Sorry for the lengthy stupid question. It was a problem with my selector. Ignore post!
I have a widget that is having public functions. that I normally am able to call like:
$("#mygrid").myGrid( "MyFunction", args );
Problem:
But in some cases the function is not getting called.
Here are some trivia
My widget is derived from another (base)widget I created.
The same function works when called for the base widget
The function is not overridden in the child widget.
The same function works for another child widget of the same parent
with the same piece of code I mentioned earlier except the name of
the widget and the DOM element.
Now, here are some important information.
A similar function in the problem widget works and when I stepped inside the call, I found a difference in both the calls in the following function in jquery-ui.js:
$.widget.bridge = function( name, object ) {
var fullName = object.prototype.widgetFullName || name;
$.fn[ name ] = function( options ) {
var isMethodCall = typeof options === "string",
args = slice.call( arguments, 1 ),
returnValue = this;
// allow multiple hashes to be passed on init
options = !isMethodCall && args.length ?
$.widget.extend.apply( null, [ options ].concat(args) ) :
options;
if ( isMethodCall ) {
this.each(function() {
var methodValue,
// Rest of the code.
Here, For my function that is not getting called, it won't enter in that this.each. when I checked the this pointer, there was a difference.
For the function that is getting called, the content of this is like the following:
0: div#myGrid
context: document
length: 1
selector: "#mygrid"
__proto__: Object[0]
But for the function that is NOT getting called, the first item and the length attribute was missing.
context: document
selector: "#mygrid"
__proto__: Object[0]
What Could be the problem?

Related

Understanding jQuery return object

I'm trying to understand how jQuery creates the return object when searching for DOM elements. I've gone through the source, but I'm not completely sure I understand, and was hoping somebody here could give me some insight.
From what I can gather reading the source, when querying for a jQuery DOM, jQuery finds matching DOM elements, and then adds the matched DOM element as an object using the index of the element as the key for the new object.
if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
for ( match in context ) {
// Properties of context are called as methods if possible
if ( jQuery.isFunction( this[ match ] ) ) {
this[ match ]( context[ match ] );
// ...and otherwise set as attributes
} else {
this.attr( match, context[ match ] );
}
}
}
return this;
Returning this, is returning the entire jQuery object which includes all the methods. Have I got it right to this point?
Now, it appears all the functions like css,find,ajax,hide,etc. are in the jQuery.fn object.
Somehow (and I think this is where I'm not seeing it), these functions are called, not on the DOM element itself, but through the access.js
https://github.com/jquery/jquery/blob/master/src/core/access.js
var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
using css as an example, we have
jQuery.extend({
css: function( elem, name, extra, styles ) {...
jQuery.fn.extend({
css: function( name, value ) {
return access( this, function( elem, name, value ) {
var styles, len,
map = {},
i = 0;
if ( jQuery.isArray( name ) ) {
styles = getStyles( elem );
len = name.length;
for ( ; i < len; i++ ) {
map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
}
return map;
}
return value !== undefined ?
jQuery.style( elem, name, value ) :
jQuery.css( elem, name );
}, name, value, arguments.length > 1 );
What I think I'm missing is how did we get from calling $('div').css(...) to that calling the jQuery.fn.extend.css method, and from there, the access method being called with a different signature to the access method initialized in the core jQuery?
Also, if we're constantly replacing the jQuery[0],jQuery[1], how is it that I can have:
var divs = $('div');
var spans = $('span');
Maintaining two different set of document tags if they are both returning the same jQuery object? I thought the object would be updated.
Am I completely misunderstanding how this is all working?
From what I can gather reading the source, when querying for a jQuery
DOM, jQuery finds matching DOM elements, and then adds the matched DOM
element as an object using the index of the element as the key for the
new object.
Yes. jQuery instances are basically array-like objects.
if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
for ( match in context ) {
// Properties of context are called as methods if possible
if ( jQuery.isFunction( this[ match ] ) ) {
this[ match ]( context[ match ] );
// ...and otherwise set as attributes
} else {
this.attr( match, context[ match ] );
}
}
}
return this;
But that's not what happens in this cited section of the code. What you see here is the code for handling the jQuery(html, attributes) signature - when the second argument is an object and the first is standalone html tag, then call the respective methods or set the attributes on the new collection (this).
Returning this, is returning the entire jQuery object which includes
all the methods. Now, it appears all the functions like css,find,ajax,hide,etc. are in
the jQuery.fn object.
Yes. The objects that are returned by the jQuery constructor do inherit these methods from the $.fn prototype object.
Somehow (and I think this is where I'm not seeing it), these functions
are called, not on the DOM element itself, but through the access.js
https://github.com/jquery/jquery/blob/master/src/core/access.js
access is just an internal helper function. All the jQuery methods are called on jQuery instances.
using css as an example, we have
jQuery.extend({
css: function( elem, name, extra, styles ) {...
jQuery.css() is just a "static", internal helper function for getting computed css values. Nothing you'd ever directly use yourself.
jQuery.fn.extend({
css: function( name, value ) {
return access( this, function( elem, name, value ) {
…
}, name, value, arguments.length > 1 );
}
What I think I'm missing is how did we get from calling
$('div').css(...) to that calling the jQuery.fn.extend.css method
There is no jQuery.fn.extend.css method. That call to jQuery.fn.extend() does define the jQuery.fn.css method. And that's just the method you call - it's prototypically inherited by $('div').
and from there, the access method being called with a different signature
to the access method initialized in the core jQuery?
No, why do you think that?
// the signature:
access = function( elems, fn, key, value, chainable, emptyGet, raw )
// the call:
access( this, // array-like collection
function(…){…}, // callback
name, // string
value, // whatever
arguments.length > 1 // boolean whether it's a getter
// undefined, implicit
// undefined, implicit
)
Also, if we're constantly replacing the jQuery[0],jQuery[1]
No, we aren't? Where did you see that?
how is it that I can have: var divs = $('div'); var spans = $('span');
Maintaining two different set of document tags if they are both
returning the same jQuery object?
They aren't. Both calls do create new jQuery instances.
I thought the object would be updated.
No, jQuery instances are quite immutable.

Passing parameters to a jQuery closure not working on Multisuggest plugin

I have a question of which someone might find this much simpler than I do, but alas, I don't have much experience with custom jQuery plugins.
The previous developer at my place of work left me with a lot of left-over plugins that don't seem to work very well, most which I've been able to fix but this which has been bugging me for a while.
It is a custom Multiple Suggestion plugin (called multisuggest) written in jQuery, and it has a set of functions that it uses internally (*e.g. setValue to set the value of the box, or lookup to update the search)*
It seems he's tried to call these plugin functions from an external script (this exteranl script specifically imports newly created suggestions into the multisuggest via user input and sets the value) like this:
this.$input.multisuggest('setValue', data.address.id, address);
This seems to call the function as it should, except the second and third parameters don't seem to be passed to the function (setValue receives nothing), and I don't understand how I can get it to pass these. It says it is undefined when I log it in the console. The functions are set out like this (I've only including the one I'm using and an internal function from multisuggest called select that actually works):
MultiSuggest.prototype = $.extend(MultiSuggest, _superproto, {
constructor : MultiSuggest,
select: function () { // When selecting an address from the suggestions
var active, display, val;
active = this.$menu.find('.active');
display = active.attr('data-display');
val = active.attr('data-value');
this.setValue(display, val, false); // This works, however when I do it as shown in the above example from an external script, it doesn't. This is because it doesn't receive the arguments.
},
setValue : function(display, value, newAddress) { // Setting the textbox value
console.log(display); // This returns undefined
console.log(value); // This returns undefined
if (display && display !== "" &&
value && value !== "") {
this.$element.val(this.updater(display)).change();
this.$hiddenInput.val(value);
this.$element.addClass("msuggest-selected");
}
if(newAddress === false){
return this.hide();
}
},
});
Why does it listen to the function, but not the values passed to it? Do I need to include an extra line of code somewhere to define these arguments?
Anyone with jQuery experience would be of great help! This is bottlenecking progress on a current project. Thanks for your time!
EDIT:
I've missed out the code of how the arguments are trying to be passed from the external script to the internal function of the plugin. Here is the plugin definition with how the external call is handled, can anyone see a problem with this?
$.fn.multisuggest = function(option) {
return this.each(function() {
var $this = $(this), data = $this.data('multisuggest'), options = typeof option === 'object' && option;
if (!data) {
$this.data('multisuggest', ( data = new MultiSuggest(this, options)));
} else if (typeof(option) === 'string') {
var method = data[option];
var parameters = Array.prototype.slice.call(arguments, 1);
method.apply(this, parameters);
}
});
};
The "usual" plugin supervisor looks like this :
// *****************************
// ***** Start: Supervisor *****
$.fn.multisuggest = 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 in jQuery.' + pluginName );
}
};
// ***** Fin: Supervisor *****
// ***************************
All the looping through this should be inside the method functions, not in the supervisor.
I'm a little worried that new MultiSuggest(...) appears in the current supervisor. That sort of thing is totally unconventional. The original author clearly had something in mind.
You need to extend the jQuery plugin function which is attached to $.fn['multisuggest'], that function is probably only taking and passing one parameter.

Passing this to jQuery plugin

I am using the autoSuggest plugin from http://code.drewwilson.com/entry/autosuggest-jquery-plugin across my website to search for various different items.
One major function with this plugin is that when a selection is added, a callback function gets called and I check whether this is new data added or it is fetched from the web service and then update some variables(which I use for submission)
Up-till now, I have been using it without any thought but now I have decide to go OOP way and create a JS object to wrap this plugin and other associated functions(common callbacks) and config values(urls) etc.
In the class I have different data variables which will be updated as the selection are added and removed. halfway through the code, I realized that the callbacks take only one argument and there is no way(apparent to me) by which I could pass those variables or the object's context to these callbacks
Below is the code I have written so far :
var coSuggest = function(options) {
this.selector = options.selector;
this.options = options;
//this.that = this;
//this.submitURL = options.submitURL;
//if y=type is not defined , default type will be people
if ( typeof (options.type) === 'undefined') {
this.type = 'people';
} else {
this.type = options.type;
}
// if as_options are not defined, as_options will be a empty object
//console.log(typeof(options.as_options));
if ( typeof (options.as_options) === 'undefined') {
this.as_options = {};
} else {
this.as_options = options.as_options;
}
this.valuesField = $('#as-values-' + this.as_options.asHtmlID);
//the initData field will be an array of object containing {id,value} of prefilled items initially
//the addData field will be an array of object containing {id,value} of new selected items from the suggestions
//the newData field will be an array of strings [values] containing the new values added
//the removeData field will be an array of objects containing {id,value} of the items from data to be removed
if ( typeof (options.initData) !== 'undefined') {
this.initData = options.initData;
this.as_options.preFill = this.initData;
}
this.as_options.selectionAdded = this.selectionAdded;
// callback function to be added in as_options as selectionAdded
this.as_options.selectionRemoved = function(elem) {
console.log(elem);
}
//return this
};
//urlConf for searching the items sitewide
coSuggest.prototype.urlConf = {
people : 'abc',
interest : "index.php/profile/getInterestsSkillsTags",
skill : 'xyz',
teacher : 'xyz',
designation : 'xyz',
city : 'xyz',
subject : 'xyz',
};
// callback function to be added in as_options as selectionAdded
coSuggest.prototype.selectionAdded = function(elem) {
//console.log($(this).find('.as-values'));
console.log(elem);
}
//bind function to bind the autoSuggest plugin with the selector provided
coSuggest.prototype.bind = function(options) {
//console.log(as_options);
$(this.selector).autoSuggest(base_url + this.urlConf[this.type], this.as_options);
}
How can I do this without losing the re-usability of the code ?
I am answering this question as I think I have solved the problem by extending the code. I added a new parameter in the selectionAdded callback and supplied the function reference in that function while calling.

JQuery plugins: Function using $(this) inside the plugin's options

Hello everyone.
I am trying to develop a Jquery plugin following the steps I found in http://docs.jquery.com/Plugins/Authoring and I seem to have problems reaching the caller object (the “this” variable) inside the options passed to the plugin. It is a plugin that I just want to use to make a button have a “blink” effect.
I would like to be able to pass the functions to execute in “show/hide” (or link blink-on, blink-off, if you prefer) as an option for the plugin. Let's say the user wants to achieve the “blinking” effect by hiding/showing the whole button every 1000 milliseconds. Then I would like the options to be something like:
$("#bttnOk").myBlinker ({
blinkHide: function(){$(this).hide();},
blinkShow: function(){ $(this).show();},
interval:1000
});
// … //
// And to make it actually blink:
$("#bttnOk").myBlinker ("blink");
Or let's say that the user wants to move the button up and down applying an inline css sytle every 200ms. Then the options would something like:
$("#bttnOk").myBlinker ({
blinkHide: function(){$(this).css(“margin-top: 10px”);},
blinkShow: function(){ $(this).css(“margin-top: 0px”);},
interval:200
});
The problem is that I seem to lose the reference to “$(this)” when I am inside the options. When the plugin reaches the blinkHide/blinkShow functions, “this” is the whole DOM window, not the button $(“#bttnOk”) my “myBlinker” plugin is attached to.
This is the first Jquery plugin I'm trying to write so I'm not even sure if there's a way to achieve what I'm trying to do.
My plugin code follows the following structure:
(function($){
var defaultOptions = {
interval: 500
}
var methods = {
init : function( options ) {
return this.each(function(){
this.options = {}
$.extend(this.options, defaultOptions, options);
var $this = $(this);
var data = $this.data('myBlinker');
// If the plugin hasn't been initialized yet
if ( ! data ) {
$this.data('myBlinker', {
on : true
});
}
});
},
destroy : function( ) { // Some code here},
blink: function ( ){
console.log("Blinking!. This: " + this);
var current = 0;
var button=this.get(0);
setInterval(function() {
if (current == 0){
button.options["blinkShow"].call(this);
current=1;
} else {
button.options["blinkHide"].call(this);
current=0;
}
}, button.options["interval"]);
}
};
$.fn. myBlinker = 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.myBlinker ' );
return null;
}
};
})(jQuery);
Any idea, correction, link or tip will be appreciated.
Thank you.
Within the setInterval function, this is the global object, not the current element DOMElement like in the blink function.
A solution to that is to save a reference of this and use this saved reference in the setInterval:
blink: function ( ){
// save a reference of 'this'
var that = this;
setInterval(function() {
// use the saved reference instead of 'this'
button.options["blinkShow"].call(that);
}, button.options["interval"]);
}
DEMO

jQuery / javascript argument handling question

First of all I don't know how to phrase the question "title", sorry if I am confusing everyone with the title here.
Anyway, I saw this code at jQuery http://docs.jquery.com/Plugins/Authoring
(function( $ ){
var methods = {
init : function( options ) {
return this.each(function(){
var $this = $(this),
data = $this.data('tooltip'),
tooltip = $('<div />', {
text : $this.attr('title')
});
// If the plugin hasn't been initialized yet
if ( ! data ) {
/*
Do more setup stuff here
*/
$(this).data('tooltip', {
target : $this,
tooltip : tooltip
});
}
});
},
destroy : function( ) {
return this.each(function(){
var $this = $(this),
data = $this.data('tooltip');
// Namespacing FTW
$(window).unbind('.tooltip');
data.tooltip.remove();
$this.removeData('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 );
My question being is that I cannot understand why do we need this if statement?
if ( methods[method] ) {
return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
}
Or in other words, in what scenario that we will pass in argument like "methods[method]" base on the example?
Thanks!
That if statement will check if you are trying to call one of the methods available to the plugin. In the case of you example you have these methods:
init, destroy, reposition, show, hide, and update
So you can do a call like :
$.tooltip('init', { arg1: true, arg2: 'a value' });
Then your code knows where to send the arguments because this if statement will be true:
if(methods['init'])
You see at the beginning that the code defines an object methods.
The function $.fn.tooltip = function( method ) accepts an argument with name method (no s at the end).
The function will execute one of the methods defined in methods, but it can only do it, if this method is also available. Hence the if(methods[method]).
The expression will be true if method is e.g. show, hide, update, etc, i.e. if the methods object has a property with the name contained in method.
Therefore the expression will be false for foo or bar. If the if statement would not be there, the code would try to call method['foo'], which does not exist and you would get an error:
TypeError: object is not a function
Is this what you wanted to know?
Your code snippet isn't complete and it doesn't contain a demo to show how it's called, so it's hard to give a definite answer.
However, here's what I think from what the code looks like:
The if statement is necessary because the tooltip function will be called with arguments such as init, destroy, show, hide, update, which refer to the functions defined in the methods hash. You probably call tooltip with init to initialize the tooltip, hide to hide it, show to show it etc. If you don't pass an argument at all, it defaults to the init method and initializes the tooltip (second branch of the if).
First of all, the piece of code declares an hashmap named methods which contains some functions.
Then, the second part declares a function named tooltip which takes a parameter named method. This parameter is the name of the function we want to call, this name is the index of this function in the methods array.
So, when you do $('#whatever').tooltip('destroy'); it will look in the methods array for the function referenced with the destroy key.

Categories

Resources