Why is my 'this' not changing context inside inner jQuery each loop? - javascript

The idea is that I want to loop through these objects and build an HTML structure which will be added to the page. I thought it would be cleaner to do it all in the chain, but apparently I'm not understanding something about the context of this as it evolves through inner loops. I've looked a bit at jQuery.proxy() a bit, but I'm not sure I understand how to apply it here. Maybe there is another way altogether of doing what I'm trying to do here...
var obj = [
{"id":1213854620001,"name":"item 1","URL":"1213897576001.jpg"},
{"id":1213854619001,"name":"item 2","URL":"1213890384001.jpg"},
{"id":1213854618001,"name":"item 3","URL":"1213890378001.jpg"},
{"id":1213854616001,"name":"item 4","URL":"1213897663001.jpg"},
{"id":1213854615001,"name":"item 5","URL":"1213897554001.jpg"}
];
$(function() {
if(obj.length) {
$("<ul/>",{id:"myID"}).append(function(){
var that = document.createDocumentFragment();
$.each(obj,function(index,dataObj){
$("<li/>",{data:{dataID:dataObj.id},text:dataObj.name}) // this === obj[index] === dataObj, shouldn't it be the [object HTMLLIElement]
.live("click",function(event) {
openVideo($(event.target).data(dataID));
})
.append(function() {
return $("<img/>",{src:dataObj.thumbnailURL})[0];
})
.appendTo(that);
});
return that;
}).appendTo("body");
}
});
function openVideo(str) {
//console.log(str);
}
The implicit question becomes, why is that empty after my loop? and how can I build this HTML structure with nested loops?
Using the suggestions from the comments, and answers, I built this, which seems to work exactly as it should, reads a little cleaner, and lets jQuery do all the javascript (e.g. documentFragment creation, and manipulation, etc):
$(function() {
if(obj.length) {
$("<ul/>",{id:"myID"})
.delegate("li","click",function(){openVideo($(this).data("dataID"));})
.append(function() {
var that = $(this);
$.each(obj,function(index,dataObj) {
$("<li/>",{data:{dataID:dataObj.id},text:dataObj.name}).each(function() {
$("<img/>",{src:dataObj.URL}).appendTo(this);
that.append(this);
})
});
}).appendTo("body");
}
});

this changes meaning as you step into new nested functions. It does not change meaning when you call $() to create a new element.
So immediately inside of
$.each(obj, function(index, dataObj) {
this is the current object over which your looping. Once you get here:
.live("click",function(event) { // <------ inside of nested function
openVideo($(event.target).data(dataID));
})
this is the element on which you clicked.
But calling
$("<li/>",{data:{dataID:dataObj.id},text:dataObj.name})
to create a new li element does not set this to the newly created element.
Also, if you want to save the meaning of this even inside of nested functions, the standard way is to save it to a new variable.
$.each(obj, function() {
var self = this;
Now self can be used instead of this as the item you're currently "on" anywhere in your loop, even in nested function handlers. Or you can pass index and dataObjects to the each function - same effect.
EDIT
As a comment pointed out, you're using live incorrectly. If you're using jQuery 1.7, you'll want:
$(document).on("click", "li", function(event) {
openVideo($(this).data(dataID));
});
in your document.ready handler. If all li's that will be clicked will be in a certain div, than select on that div instead of document. If you don't want this click handler to apply to all li's, but only some, then decorate all the li's you want this click handler to apply to with a css class, then instead of passing the filter "li" to on, you'd pass li.yourClass

$.each sets this for the inner function to be the element iterated (and yes, this is one of the complicated things in Javascript, but your comment can be explained by just referencing each behaviour).

The thing is, in prototype based languages, which javascript is one, functions are objects, and objects have properties. So when you use the 'this' keyword, it reverts to the scope of the current prototype. Inside of an 'inline function' (which is actually not a concept in javascript since functions are just objects), this refers to the function.

Related

what does wrapping a jQuery object with another `$` mean? [duplicate]

I am currently working through this tutorial: Getting Started with jQuery
For the two examples below:
$("#orderedlist").find("li").each(function (i) {
$(this).append(" BAM! " + i);
});
$("#reset").click(function () {
$("form").each(function () {
this.reset();
});
});
Notice in the first example, we use $(this) to append some text inside of each li element. In the second example we use this directly when resetting the form.
$(this) seems to be used a lot more often than this.
My guess is in the first example, $() is converting each li element into a jQuery object which understands the append() function whereas in the second example reset() can be called directly on the form.
Basically we need $() for special jQuery-only functions.
Is this correct?
Yes you only need $() when you're using jQuery. If you want jQuery's help to do DOM things just keep this in mind.
$(this)[0] === this
Basically every time you get a set of elements back jQuery turns it into a jQuery object. If you know you only have one result, it's going to be in the first element.
$("#myDiv")[0] === document.getElementById("myDiv");
And so on...
$() is the jQuery constructor function.
this is a reference to the DOM element of invocation.
So basically, in $(this), you are just passing the this in $() as a parameter so that you could call jQuery methods and functions.
Yes, you need $(this) for jQuery functions, but when you want to access basic javascript methods of the element that don't use jQuery, you can just use this.
When using jQuery, it is advised to use $(this) usually. But if you know (you should learn and know) the difference, sometimes it is more convenient and quicker to use just this. For instance:
$(".myCheckboxes").change(function(){
if(this.checked)
alert("checked");
});
is easier and purer than
$(".myCheckboxes").change(function(){
if($(this).is(":checked"))
alert("checked");
});
this is the element, $(this) is the jQuery object constructed with that element
$(".class").each(function(){
//the iterations current html element
//the classic JavaScript API is exposed here (such as .innerHTML and .appendChild)
var HTMLElement = this;
//the current HTML element is passed to the jQuery constructor
//the jQuery API is exposed here (such as .html() and .append())
var jQueryObject = $(this);
});
A deeper look
thisMDN is contained in an execution context
The scope refers to the current Execution ContextECMA. In order to understand this, it is important to understand the way execution contexts operate in JavaScript.
execution contexts bind this
When control enters an execution context (code is being executed in that scope) the environment for variables are setup (Lexical and Variable Environments - essentially this sets up an area for variables to enter which were already accessible, and an area for local variables to be stored), and the binding of this occurs.
jQuery binds this
Execution contexts form a logical stack. The result is that contexts deeper in the stack have access to previous variables, but their bindings may have been altered. Every time jQuery calls a callback function, it alters the this binding by using applyMDN.
callback.apply( obj[ i ] )//where obj[i] is the current element
The result of calling apply is that inside of jQuery callback functions, this refers to the current element being used by the callback function.
For example, in .each, the callback function commonly used allows for .each(function(index,element){/*scope*/}). In that scope, this == element is true.
jQuery callbacks use the apply function to bind the function being called with the current element. This element comes from the jQuery object's element array. Each jQuery object constructed contains an array of elements which match the selectorjQuery API that was used to instantiate the jQuery object.
$(selector) calls the jQuery function (remember that $ is a reference to jQuery, code: window.jQuery = window.$ = jQuery;). Internally, the jQuery function instantiates a function object. So while it may not be immediately obvious, using $() internally uses new jQuery(). Part of the construction of this jQuery object is to find all matches of the selector. The constructor will also accept html strings and elements. When you pass this to the jQuery constructor, you are passing the current element for a jQuery object to be constructed with. The jQuery object then contains an array-like structure of the DOM elements matching the selector (or just the single element in the case of this).
Once the jQuery object is constructed, the jQuery API is now exposed. When a jQuery api function is called, it will internally iterate over this array-like structure. For each item in the array, it calls the callback function for the api, binding the callback's this to the current element. This call can be seen in the code snippet above where obj is the array-like structure, and i is the iterator used for the position in the array of the current element.
Yeah, by using $(this), you enabled jQuery functionality for the object. By just using this, it only has generic Javascript functionality.
this reference a javascript object and $(this) used to encapsulate with jQuery.
Example =>
// Getting Name and modify css property of dom object through jQuery
var name = $(this).attr('name');
$(this).css('background-color','white')
// Getting form object and its data and work on..
this = document.getElementsByName("new_photo")[0]
formData = new FormData(this)
// Calling blur method on find input field with help of both as below
$(this).find('input[type=text]')[0].blur()
//Above is equivalent to
this = $(this).find('input[type=text]')[0]
this.blur()
//Find value of a text field with id "index-number"
this = document.getElementById("index-number");
this.value
or
this = $('#index-number');
$(this).val(); // Equivalent to $('#index-number').val()
$(this).css('color','#000000')

Writing JavaScript Functions - Should I Pass jQuery Objects as arguments?

I'm curious from both a performance and a "best practices" standpoint. I'm using a lot of jQuery in my JavaScript, and often find myself passing jQuery objects as arguments into functions I'm writing. Is it more efficient/effective to pass the selector string rather than the actual jQuery object as the argument? Is this just a stylistic difference, or are there any good reasons to use one method over the other?
Using jQuery objects in arguments:
function updateSelectOptions(optionsData, $selectElement) {
// function code
}
Or using a selector string as the argument:
function updateSelectOptions(optionsData, selectorString) {
var $selectElement = $(selectorString);
// function code
}
You should accept anything the jQuery constructor can for maximum flexibility.
Re-wrapping a jQuery collection doesn't blow up, so I often use something like...
var fn = function(elems) {
var $elems = $(elems);
};
That way, you can accept a jQuery collection, selector string or reference to native DOM element(s).
If you find yourself wanting to write a function that takes a jQuery object as a parameter, why not just make your function a jQuery plugin? It's pretty easy, and it makes using the function fit in with the rest of your jQuery code.
Instead of
function something(jq) {
jq.css("color", "red");
});
you'd write
$.fn.something = function something() {
this.each(function() {
$(this).css("color", "red");
});
return this;
};
Now when you want to turn something red, you can just say
$(".whatever").something();
The value of this in a jQuery plugin is the jQuery object that's being passed along the chain. You don't have to wrap it with $(this). Unless your function is something that returns some value, it's good to return whatever's passed in so that you can use your own plugin in the middle of a dotted chain.
In my opinion, passing the object is fine and would be better for performance.
Why?
Most of the time, the reason for using functions is to reuse code. Hence, if you pass the string (the selector) such as updateSelectOptions(optionsData, selectorString)
every time you call the function and then use that string to select the element:
var $selectElement = $(selectorString);
This will consume more memory, because the element will have to be searched for every function call.
Where if you pass the cached object this element will only be selected and searched for only once.
The second approach remove any reference to the object after the function finished to execute. The first one allow you to keep the reference to the object to manipulate it outside the scope of the function.

using this rather than $(this) in an .each() call

I've done quite a lot of reading this past day to get a deeper understanding of this vs. $(this) and how JS determines this as it interprets, but I still can't figure out one detail of a plugIn I'm analyzing to deepen my knowledge:
$.fn.plugInName = function(options) {
return this.each(function() {
new $.plugInName(this,options);
});
};
Everything I've read indicates that although this.each() is used to call JQuery.prototype.each(), each object should be referred to as $(this) within the each() function, but the above uses regular ol' this, and I can't figure why. The $.plugInName declaration looks like this:
$.plugInName = function(el,options) {
...
}
Any insights anyone may have will be a big help.
EDIT: Here's the full source on GitHub
From MDN:
When a function is called as a method of an object, its this is set to
the object the method is called on.
When you call something like
$('#myDiv').plugInName()
the function is called as a method of the jQuery object $('#myDiv'). So, in this case, this already refers to a jQuery object, thus we don't need the extra $() wrapper.
A common case when we do need $() is when binding events.
$('#myDiv').on('click', function(){
alert('You clicked my div!');
});
The difference here is that the function is called not on $('#myDiv'), but the DOM element that it belongs to. So, here this is a DOM object, not a jQuery object.
Inside the callback function passed to each, this refers to the DOM element, it is not a jQuery object.. which is just what $.plugInName wants. It expects a DOM element.

When to use "$" in javascript/jQuery

Ive been starting to do javascript and jQuery recently and one thing I constantly find myself wondering is when to use "$" I know that indicates jQuery but it just doesn't always seem to be that way. I'll give some examples:
These are two scripts I've written:
First:
$(function() {
var newHTML = '<span style="font-size: 1.7em; text-align:center; line-height:50px;">Login</span>';
var oldHTML = '<span style="font-size: 32px; line-height: 18px;">+</span><span style="font-size: 14px; float: left;">Add to watchlist</span>';
// on mouse over
$("a.bid-addwatchlist").hover(
function () {
(this).innerHTML = newHTML;
},
// on mouse out
function () {
(this).innerHTML = oldHTML;
});
});
Second:
(function(){
$("#container a").click(function(){
if ($(this).html() == "Stop Listening")
{
$(this).html("Listen");
}
else if ($(this).html() == "Listen")
{
$(this).html("Stop Listening");
}
});
});
Why is it that in the first script it wouldn't work if I had a $ before "this" but the second script needed it?
Note: I did already look here: When to use $ and when not to
But that answer was not nearly comprehensive enough.
$(function(){
// ...
});
is equivalent to:
$(document).ready(function () {
// ...
});
This is because jQuery itself will execute the function on the ready event.
On the other hand the below creates an anonymous function that isn't executed:
(function(){
// ...
});
What you'd need instead is:
(function(){
// ...
})();
and that would execute the function immediately.
The first script doesn't actually need the $(this) unless you want to use jQuery functions as the second function one does.
Although it is good to note that the second function isn't executed as stated above unless you have other code that isn't shown here.
You use $ when you NEED a jQuery object or method in order to solve your problem. If you have a DOM element already and a direct DOM property will give you everything you need, then there's no reason to use jQuery. On the other hand, if you want to use a jQuery method that is unique to jQuery, then, of course, you create a jQuery object and call a method on it.
Example:
$("#myButton").blur(function() {
if (!this.value) {
this.value = "Enter name";
}
});
So, in the first line of this script, we use a jQuery object because it's easier to find an object in the page and assign an event handler.
Inside the handler, I'm using plain javascript and direct properties because there's no advantage to using jQuery. This could have been written this way:
$("#myButton").blur(function() {
if (!$(this).val()) {
$(this).val("Enter name");
}
});
But, there's no advantage to using jQuery inside this handler. It's just more code and more function calls.
So ... use it when you need it or it makes more readable or more reliable code. If none of those apply, don't use jQuery for that particular operation.
JQuery objects are meant to represent a DOM element. In the second example, you are using "$(this)" followed by a function call. This means that you can use only the interfaces support by the JQuery object.
In the first example, you are just using "this", which is a reference to the DOM element that owns the current execution context. DOM elements do not implement the JQuery interface (or else you wouldn't need to use JQuery at all!)
In other words, JQuery maintains enough information about a DOM element to be able to find it and perform manipulations on it. However, that does not mean that JQuery exposes an interface identical to a DOM elements.
EDIT: DOM stands for Document Object Model. Essentially, this is a model where all elements in the document are represented as objects (DOM elements). For example, you could use document.getElementById("anId") to retrieve a "DOM element" by the value of its ID attribute. This Document Object Model gives us the ability to manipulate and alter various parts of the document via scripting languages. Basically, the DOM is just a bunch of hierarchical objects that represent the elements on your page. These objects expose interfaces like "innerHTML"--Since JQuery objects are not DOM objects, they do not
look at jquery source code
jQuery.fn = jQuery.prototype = {
....
// HANDLE: $(expr, context)
// (which is just equivalent to: $(context).find(expr)
} else {
return this.constructor( context ).find( selector );
}
// HANDLE: $(function)
// Shortcut for document ready
} else if ( jQuery.isFunction( selector ) ) {
return rootjQuery.ready( selector );
}
if (selector.selector !== undefined) {
this.selector = selector.selector;
this.context = selector.context;
}
return jQuery.makeArray( selector, this );
....
}
$(handler) the handler will execute when DOM is loaded, if DOM already loaded, handler will execute immediately.
(handler) just declare an anonymous function, if you want to execute it you should use (handler)() as James Khoury's answer;
In the first case, this is a DOM element on which the function is called. You're using the innerHtml property directly.
In the second case, you're wrapping the DOM element in a jQuery wrapper and using jQuery methods such as .html() and you're therefore manipulating the element's innerHtml indirectly.

Can someone help explain this jQuery code?

$('#AddMoreErrors,#AddMoreMime,#AddMoreRemote').live('click',function(){
var newRow = $(this).closest('tr');
$(this).closest('tr').after('<tr class="'+newRow.attr('class')+'">'+newRow.html()+'</tr>').find('span').removeAttr('id').addClass('removetr').html('Del');
});
I wrote the above jquery code and this is what it does: When someone clicks on "add more" it finds the nearest tr, replicates it into another tr and appends it to the first one. Then it finds all the spans, removes its id and adds a new class and changes the text.
Now what surprised me was find('span').removeAttr('id').addClass('removetr').html('Del');
I know I could do removeAttr on span but how did addClass and html also get applied on span?
jQuery returns object of element after each operation on which it is performed, and execution order are from left to right. So its .removeAttr('id') will be on span and that span object will be returned. Similarly .addClass('removetr') and .html('Del'); are performed.
Most jQuery methods return this, so:
find('span').removeAttr('id').addClass('removetr').html('Del');
Means the same as:
var foo = find('span');
foo.removeAttr('id');
foo.addClass('removetr')
foo.html('Del');
I'm not sure what you're asking, but if you want to know why you can chain jQuery commands one after another, that's because every jQuery function return "this". You can easily do the same when working with IIFEs:
function doSth() {
// Some logic
return this;
}
What actually happens the is, that you return the object after each method call and continue working on the same object you called the method from before.
That means that
$("#sel").method().method2().method3();
is exactly the same as writing
var sel = $("sel");
sel.method();
sel.method2();
sel.method3();
Only that it's shorter, looks nicer and you don't need to save the variable in case you don't need it again afterwards.
That way, you can write one function after another and you'll always have the complete jQuery function after the function is executed.
So you also see, why all three methods are executed on the same object: It's the way chaining works. By calling "$" you create a new object. And all methods are then executed on that object.
Unless of course, the function actually returns a value. Then, this won't work.
function getSth() {
return "value";
}
This is called method chaining. Each of those jQuery methods (removeAttr(), addClass(), html(val)) returns the jQuery object to which it belongs. This means you can call further methods using the dot notation.

Categories

Resources