Is this phrase always true?
$("p").click(function(event) {
alert( event.currentTarget === this );
});
Is one method preferred over the other? I like to use $(this) instead of event.currentTarget but can one do better in some conditions? Which is better? Are absolutely the same?
And another nuance - When checking on firebug console.log(this) and console.log($(this)) gives me exactly the same element. If they are the same - what is different? (since I know I can write this $(this).css('color','white') but can't write this.css('color','white')
Generally, yes, it will be the same. You can make it different by using $.proxy to manipulate the context, but in practice you probably never will.
$(document.body).on('click', $.proxy(function(e) {
console.log(this); // window
console.log(e.currentTarget); // document.body
}, window));
As to the other question, this is a native DOM element, whereas $(this) is a jQuery object wrapping that DOM element. The jQuery wrapper means you can run jQuery functions such as css, which are not available on native DOM elements.
And, to answer the precise wording of your question, event.currentTarget is normally equal to this, not to $(this).
Part of your answer is above. I hope its clear enough.
No console.log(this) and console.log($j(this)) will not give you the same result. $(this) converts this to a jQuery Object and hence you can call .css like methods which can be called on jQuery objects($(this)) and not the HTML elements which will be this.
This property will typically be equal to the this of the function.
If you are using jQuery.proxy or another form of scope manipulation, this will be equal to whatever context you have provided, not event.currentTarget
Related
In majority of JavaScript method(iterator) callback first argument is element and second is index. But in case of jQuery always second argument is element and first one is index. Why they formatted differently? Is that for avoiding confusion between them, since both contains methods like map, filter, etc. Is there any special meaning behind that? I'm just curious to know.
This is because jQuery relies on this; it doesn't need an element iterator.
$('selector').each(function(){
// 'this' refers to unwrapped jQuery selector element; no need for arguments == cleaner code
var $this = $(this);
});
In Vanilla JS, iterators rely on the parameters:
[].forEach(function(el, index){
// 'this' refers to Window object; use the argument
el.querySelector('foo')
});
Because jQuery can utilize the scoped element using this, it doesn't really need the arguments. I'd have to look up the history of the library to even see if the element existed as the second argument in earlier versions.
In jQuery, when I call:
$("selector").a_function(function(){
this.toggleClass("a-class");
}
I am told that this is of a certain type, and does not have the jQuery function available to it. The problem is that I am not getting jQuery objects returned to me. The way I am getting around this is to do:
jquery_object = jQuery(this);
every time. I thought that $() is equivalent to calling jQuery and that I am supposed to receive a jQuery object.
Is this normal? What am I doing wrong here?
You'll want to use $(this) instead of just this.
In the case you gave this doesn't refer to the DOM element.
By wrapping it in the standard JQuery selector function, you'll actually be calling to the DOM element.
I use this a lot for click handlers.
$('a').click(function(e) {
$(this).toggleClass('a-class');
});
Pretty much any function in JQuery that gets called through a selector and has a callback will use this format.
The jQuery documentation states that you need to pass in a selector as a string to the .on() method. For example:
$('#someEl').on('click', '.clickable', function() { /* ... */ });
However, it SEEMS to work when you pass in an individual node as well:
var someNode = getElementById('someNode');
$('#someEl').on('click', someNode, function() { /* ... */ });
When I tried passing in a jQuery object, it sort of failed out as far as I can tell, and treated it as a direct binding instead of a delegated binding:
var $someNode = $('#someNode');
$('#someEl').on('click', $someNode, function() { /* ... */ });
// seemed to act as:
$('#someEl').on('click', function() { /* ... */ });
So I guess the questions are:
Is passing in a DOM node just not a documented part of the API? Or did I miss it in the API docs?
Is there a benefit to caching the node (not the jQuery object-wrapped node), or does jQuery ultimately do the same amount of work? (in other words, I can assume when I pass a selector string that it parses it, finds the valid nodes, and then performs the binding... but if I provide it a nice fresh DOM node will it pass on this stage, or does it still for some reason wrap things up in jQuery before going to work?)
Am I wrong about the jQuery object being an invalid candidate? Did I just miss something in my testing? It seems silly that if I'm already caching jQ objects, that I'd have to supply a selector again (making it do the whole selection process again) rather than being able to use what I've already done...?
Delegation serves two purposes:
Setting a single event handler on a parent element for multiple children that share the same logic when the event is triggered. This is supposed to consume less memory, but should only make a noticeable difference when used to replace a large number of individual event handlers. I suspect this is what you're trying to achieve.
Defining event handler for elements that do not exist in the DOM at the time of the binding.
Now, to answer your questions:
Passing a DOM node is not documented, so it shouldn't be used. Although you said it works, jQuery is fooling you. By looking at the source code, it only seems to work because of event bubbling, but the this value inside the handler will be the container (see demo).
You said:
I can assume when I pass a selector string that it parses it, finds the valid nodes, and then performs the binding
That's a wrong assumption. You're always binding to the element (or elements) you're calling .on at. The selector you pass is only used to check if it matches the event object's currentTarget property, which is provided by the browser.
The same I said in #2 applies here: jQuery won't select anything based on the selector you passed, it will just check the currentTarget against that selector.
To answer your main question, no, selectors are supposed to be a string, or undefined. What you're seeing is a quirk of how jQuery tries to guess which calling convention you are using - more on this in a bit.
There's no way to pass a DOM element instead of a selector, sadly. If delegate is the element that your handler was bound to, and target is the element that fired the event, jQuery will always search delegate's descendants using the selector provided, and check if target is in the matched selection. If jQuery allowed some way to pass DOM nodes instead of a selector, there definitely would be a performance benefit.
Well, in the usage $('#someEl').on('click', '.clickable', handler) you've never selected elements matching .clickable, and neither would jQuery at that stage, so you're not doing the work doubly there.
You can call .on()in multiple ways, especially since there are multiple optional parameters(selector, data).
When you call .on(type, notAString, handler) jQuery assumes you are using the calling convention: .on(type, data, handler) - which it translates to .on(type, undefined, data, handler).
Here is a demonstration of what your suggested calls do:
http://jsfiddle.net/BGSacho/HJLXs/1/
HTML:
<div id="someEl">
<div id="someNode" class="clickable">Click me!</div>
</div>
JavaScript:
$(document).ready(function () {
function display(event) {
console.log("event.currentTarget", event.currentTarget);
console.log("event.delegateTarget:", event.delegateTarget)
console.log("event.data:", event.data);
}
$('#someEl').on('click', ".clickable", function (event) {
console.log("I am .on(type, selector, fn) - my currentTarget should be .clickable and my delegateTarget should be #somEl - this demonstrates that jQuery is using the event delegation mechanism. I have no data bound to my event.");
display(event);
});
$('#someEl').on('click', document.getElementById('someNode'), function (event) {
console.log("I'm .on(type, data, handler) - my currentTarget should be #someEl because I'm not using a delegation mechanism. I also have some data bound.")
display(event);
});
$('#someEl').on('click', $('#someNode'), function (event) {
console.log("I'm still .on(type, data, handler)");
display(event);
});
});
They might all seem to work because you don't use this(aka event.currentTarget) in your handling code. I'm not sure why you are getting different results with a jQuery object and a DOM node.
What's the jQuery equivalent for each():
$(".element").each(function(){
// do stuff
});
when attaching a function to a single element, like #element ?
You can always reference the jQuery object in a variable:
var $el = $('#element');
...then manipulate it.
$el.doSomething(); // call some jQuery methods from the cached object
$el.doSomethingElse();
If the reason you wanted .each() was to reference the DOM element as this, you don't really need the this keyword to do it, you can simply grab the DOM element out of the jQuery object.
var element = $('#element')[0]; // both of these give you the DOM element
var element = $('#element').get(0); // at index 0
The two of these are equivalent, and will retrieve the DOM element that would be referenced as this in the .each().
alert( element.tagName ); // alert the tagName property of the DOM element
alert( element.id ); // alert the ID property of the DOM element
I'd note that it isn't necessarily bad to use each to iterate over a single element.
The benefits are that you have easy access to the DOM element, and you can do so in a new scope so you don't clutter the surrounding namespace with variables.
There are other ways to accomplish this as well. Take this example:
(function( $ ) {
// Inside here, "this" will refer to the DOM element,
// and the "$" parameter, will reference the jQuery library.
alert( this.tagName );
// Any variables you create inside will not pollute the surrounding
// namespace.
var someVariable = 'somevalue'; // is local to this function
}).call( $('#element')[0], jQuery );
To directly answer your question, .each() operates normally on element sets of any size including 1.
You can also omit the .each() call completely and just call jQuery methods on $('#element'). Remember that you can chain most if not all jQuery method calls as they return the jQuery object. This even works on multiple elements for the matter, depending on what the methods do.
$('#element').doSomething().doSomethingElse();
If you need to reference the object multiple times, make a variable:
var $elm = $('#element');
$elm.doSomething();
doSomethingElse($elm);
Use first().
each() matches all elements, while first() matches only the first.
There are other selectors too. When you use the id in the selector, you will only get one element. This is the main difference between .element and #element. The first is a class that can be assigned to many elements, while the second is an id that belongs to only (at most) one element.
You can still use each if only one (or 0) element is returned. Also, you can skip each altogether if you want to link an event. You use each when you want to execute a specific function for each element in the list of elements.
If there is only 1 element, you can access it normally using the selector.
$('#your_element').your_event(function() {
});
Behind the scenes, each is just a for loop that iterates through each element in the map returned by jQuery.
It is essentially† the same as:
var i, map = $('...selector...');
for (i = 0; i < map.length; i++)
{
someFunction(i, map[i]);
}
† There's more to it than this, involving calling the function in the context of the map element etc.
It's implementation is to provide a convenient way to call a function on each element in the selection.
Do you mean like $('#element').children().each()
supposing you have something like a ul with an id and you want the li each inside it?
If the intention is to call a (non-jQuery) function in a new scope I think using "each" still is a valid (and probably the most elegant) way and it stays true to the jQuery syntax although I agree to Alex, it feels wrong since it might have some overhead.
If you can change the syntax use $('#element')[0] as replacement for this (as already mentioned in the accepted answer).
Btw could someone with enough reputation please correct the comment of "zzzzBov" about the accepted answer?
$('#element')[0] and $('#element').get(0) ARE! the same, if you want the jQuery object use $('#element').first() or $('#element').eq(0)
I have a js function which has, until now, always been the callback for a click event, and therefore relies heavily on the 'this' pseudo-variable. 'this' is a <li> element.
However I now have a circumstance where it is sometimes triggered using more convoluted route, and in these circumstances 'this' is an entirely different element. However, before calling the function I am able to find the relevant <li>, but is there a way I can feed this in as 'this'? I thought of using .each() on the <li>, but it won't work on a single element.
edit it turns out that each() does work on single elements.. the error turned out to be something else.
Haven't deleted this question though as it could be useful to others
You are looking for the call method:
function onClick() {
console.log(this); // will be the #test element in both cases
return false;
}
$('#test').click(onClick);
$('#test2').click(function() {
onClick.call($('#test')[0]);
return false;
});
Although this is also possible with apply:
onClick.apply($('#test')[0]);
The [0] is necessary to pass the actual DOM element instead of the jQuery wrapped set.
try using jquery.callback plugin. It keeps the context of callback.