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.
Related
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?
I just want to share an experience with you all. So my problem was, that I came across the problem of binding javascript back-end objects to HTML front-end elements. Now, i have searched through google and read some stackoverflow articles about this problem, and many posts answer to this is to use jQuery.data(), however in my first attempts I did not succeed because there was something I did not know about jQuery's object creation method. My problem was that, I wanted to retrieve the stored data outside of the scope where i stored this, and jQuery always(i think) returns a new object reference when i write jQuery('selectorID'). So for example:
var ref1 = $('#myID');
var ref2 = $('#myID');
if(ref1 == ref2)
{
alert(true);
}
else
{
alert(false);
}
Will always alert false. However replacing the jQuery method with javascript's built-in getElementById() we will get the same reference! Hence the following code will always alert true!
var ref1 = document.getElementById("myID");
var ref2 = document.getElementById("myID");
if(ref1 == ref2)
{
alert(true);
}
else
{
alert(false);
}
The little morale of my story is that if you want to globally bind javascript objects to HTML elements and are thinking about using jQuery's data() method, store the data on the reference returned by javascript's built-in getElementById(). That way, whereever you retrieve the reference with getElementByID, you will always get the same reference and can get the data from it using jQuery's data() method.
My Questions:
Is my logic of thinking ok?
Is there a better way to globally bind javascript objects to HTML elements?
Whatever the reason behind the code you mention not working was, it was decidedly not the fact that jQuery gives you a new collection for every query. Given the HTML:
<div id="somediv"> foo bar </div>
the following Javascript works as expected:
var $ref1 = $('#somediv');
var $ref2 = $('#somediv');
console.log("$ref1:", $ref1);
console.log("$ref2:", $ref2);
// compare the collections / references
console.log("$ref1 == $ref2:", $ref1 == $ref2); // => false
console.log("$ref1 === $ref2", $ref1 === $ref2); // => false
// compare the referred DOM elements themselves
console.log("$ref1[0] == $ref2[0]:", $ref1[0] == $ref2[0]); // => true
console.log("$ref1[0] === $ref2[0]", $ref1[0] === $ref2[0]); // => true
$ref1.data('somedata', 'SOMEDATA');
console.log('$ref1->somedata:', $ref1.data('somedata')); // => SOMEDATA
console.log('$ref2->somedata:', $ref2.data('somedata')); // => SOMEDATA
The way I do it is something like this.
var ref1 = $('a');
var ref2 = $('div');
var equal = ref1.length === ref2.length;
if (equal) {
$.each(ref1, function(i) {
equal = equal && ref1[i] === ref2[i];
if (!equal) {
return false;
}
});
}
if (equal) {
alert(true);
} else {
alert(false);
}
ref1[0] === ref2[0] // Should return true
I think jQuery's instances are unique, so you can compare its matched items, which should be just one element when you reference an ID.
You could do something like this:
var ref1 = $('#myID')[0];
var ref2 = $('#myID')[0];
I dive into jQuery's source code, and find the constructor of jQuery. As follow:
// Define a local copy of jQuery
jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context, rootjQuery );
}
Whenever you use ref2 = $('#myID') to retrive a corresponding jQuery element, jQuery will create a new object to you. So the == will return false to you coz' the two element is completely different to js object engine.
Seems getElementById method is more fit your need. But I don't know how js engine perform its getElementById method.
Every time I need a static variable, I end up tacking it on as a property to the object that uses it and needs it to persist.
Particularly, this index here(MT.MAOrb.startR.index) I need to be static or hold it's value until the function is called again by a callback.
Is using this form the best way to do this?
MT.MAOrb.startR.index
/**
** MAOrb
*/
MT.MAOrb =
{
pre : function() {
var o_p = {
model : 'MAOrb'
};
return o_p;
},
post : function( o_p ) {
MT.MAOrb.startR( o_p );
},
startR: function( o_p ){
var sky = document.getElementById( 'Ab1' );
if( MT.MAOrb.startR.index === undefined ) {
var size = Object.size( o_p );
console.log( 'size' + size );
var index1 = MT.MAOrb.random( 0, size - 1 );
console.log( 'index1' + index1 );
MT.MAOrb.startR.index = index1;
MT.MAOrb.startR.o_p = o_p;
}else{
MT.MAOrb.startR.index++;
}
var image_element = MT.MAOrb.makeElement( MT.MAOrb.startR.o_p[ MT.MAOrb.startR.index ] );
sky.appendChild( image_element );
MT.MAOrb.moveLinear( image_element );
},// ....more code here
};
If you are trying to emulate a public static property, then that's a totally A-OK way to do it.
JavaScript is not a classical object oriented language. It is prototypical.
One ramification is that there really isn't a concept of static in the language.
The way you're doing it is totally fine, as long as you don't mind that another object can directly read and modify the property.
Javascript has no concept of static vs. non-static variables: everything is just a property of an object. As such, there is no right or wrong way of doing static variables, only right or wrong ways of doing static-like variables.
That being said, adding the variable as a property of a fixed (module-like) object, as you're doing, is pretty much your best bet (ie. best practice). Unless you're using a library like Backbone.js that is, which actually does add support for static variables to its "models" (ie. it's class system).
I'd say that it's actually a rather strange way to do it. Javascript provides function level scope, and you can use that to your advantage by using an immediately-invoked-function-expression (IIFE):
myObject = {
count: (function () { // this function is invoked immediately
var staticCounter = 0;
return function () { // this is the actual 'count' function
return ++staticCounter;
};
}())
};
myObject.count(); // 1
myObject.count(); // 2
One reason that this could be considered a better approach is that it completely hides the static variable. If you were to do myObject.count.staticCounter, some other code might be reading or writing to that variable. Obviously you wouldn't want that to happen, but if you do this, you are completely guaranteed of that variable's scope, which leads to easier debugging and refactoring in the future.
I was wondering if it were possible to pass parameters to a javascript event called from an AJAX extender (in this case AutoComplete) while preserving the EventArgs also being passed. Here's a short sample of what I mean:
<... OnClientItemSelected="DoThis(somevar, eventargs);" />
Script:
function DoThis(somevar, eventargs) {
var blah = somevar;
...
var blah2 = eventargs.get_text();
}
That's the idea. Does anyone know how to do this or if it's possible?
It's not exactly clear what your asking, but...I'll take a stab at it anyway. It sounds like you essentially want to augment the event data passed to the event handler. There are a number of ways to do this:
If you are using jQuery:
First, the bind() method can be invoked with either 2 or 3 arguments:
bind( eventName , eventHandler )
bind( eventName , data , eventHandler )
If bind() is invoked with 3 arguments, the second argument, data, is an arbitrary object handed to the event handler as the .data property of the Event instance passed to the event handler.
Second, the event handler is a closure, the lexical scope of which includes those variables that were in scope at the point the closure was created. For instance, the usual way you would bind an event handler to an event in jQuery is something like this:
$(function(){
var pi = 3.1415926 ;
var phoneticAlphbet = { a = "alpha" , b = "bravo" , c = "charlie" ,
...
x = "x-ray" , y = "yankee" , z = "zulu"
} ;
$('input:radio[name=buttonset1]').click(function(){
// at this point, the variables pi and phonetic alphabet are in-scope,
// as well as any other globals you've created
// do something here
}) ;
}) ;
So that's a second way to pass data to the event handler.
A third way is to hang data off DOM tree elements. jQuery conveniently provides the data() method to do this:
.data( name , value ) sets a value on the wrapped set
.data( name ) retrieves values from the wrapped set
For example, this snippet will save the original value for each and element on the page:
$('input,select').each(function(){
var item = $(this) ;
item.data( 'original-value' , item.val() ) ;
}) ;
Having set it, the value can be retrieved:
$('input#password').change(function(){
var item = $(this) ;
var oldValue = item.data('original-value') ;
var newValue = item.val() ;
if ( oldValue == newValue )
{
alert('the new password must be different than the old password!') ;
}
}) ;
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.