Cached this, jQuery [duplicate] - javascript

This question already has answers here:
var self = this?
(8 answers)
What is the rationale behind the "var self = this" approach? [duplicate]
(4 answers)
Closed 9 years ago.
I often see the code like this one:
$('.element').on('click', function(event) {
var element = this;
// somecode that uses "element"
});
Is there any reason to cache this?

This is necessary if the code contains a closure that needs to access this. this is not a local variable, so it will not be captured in the closure, you need to bind a local variable for that purpose.
You also need it if the code contains a loop using a function like $.each(), and the body of the loop needs to refer to the element. These functions rebind this in the body to the current iteration object.

A lot of people set a reference to this if they need to reference it in another scope of code. For example:
$('.element').on('click', function(event) {
var element = this;
// somecode that users "element"
function foo() {
//$(this).text() WONT Work!
alert($(element).text()); //references the element that was clicked
}
});

Once you are inside a function or loop, this might refer to an object within that function. Therefor explicity assigning the element allows you to always access it, independent of the scope.

this is not a jQuery element, wrap it in $(this).
Caching is good because it stores the element, and it doesn't take up memory or processing time trying to re-find the element. However, this changes on scope so you might not want to cache that one.

Related

Why is it not encouraged to create functions within a loop in JavaScript? [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 6 years ago.
I am an absolute newbie, and I just read this in JavaScript: The Good Parts.
In the chapter talking about scope, it says "It is important to understand that the inner function has access to the actual variables of the outer functions and not copies in order to avoid the following problem." And then the two following examples look like this:
//BAD EXAMPLE
var add_the_handlers = function (nodes) {
var i;
for (i = 0; i < nodes.length; i += 1) {
nodes[i].onclick = function (e) {
alert(i);
};
}
};
//END BAD EXAMPLE
var add_the_handlers = function (nodes) {
var helper = function (i) {
return function (e) {
alert(i);
};
};
var i;
for (i = 0; i < nodes.length; i += 1) {
modes[i].onclick = helper(i);
}
};
According to the author the second example is better because it doesn't use a loop inside the function, otherwise it could be wasteful computationally. But I am at loss and don't know what to do with them. How do I put his theory in real application? Can anyone illustrate these two examples combine HTML?
The problem is with closure. The inner functions have access to the variable i defined outside of these functions. After all iterations of the loop have been executed, the variable i will hold the value of nodes.length. So when you click on nodes[0], the alert will say nodes.length, which is not what you'd expect. (You would expect the alert to say 0.) The same holds when you click on nodes[1], nodes[2], etc. The alert for all of them will say nodes.length.
Firstly, in the bad example, a function is created for each event handler; the loop creates multiple function objects. Whereas in the second example, a single function is created and referenced from inside the loop. So you save a lot of memory.
Secondly, in the bad example, as the value of "i" runs, the function does not retain the value, and when it runs, it will always return the last value of "i". In the good example however, as "i" is passed into the function, this value is retained as the lexical environment of the function, and when it is called, it will return the correct value.
Thirdly, as mentioned by #Gary Hayes, we might want to use the function elsewhere too. So it's best to keep it independent of the loop.
You can check it with HTML working here: https://jsfiddle.net/vdrr4519/.
'multifunc' elements are inited with example with many functions, 'singlefunc'—with a single one. See, we take all the elements with a class and pass them to the function.
multifunc(document.querySelectorAll('.multifunc'));
Function runs 'for' loop and adds 'click' event listener. So the element should alert its index on click (beginning from 0). But in example with many function a wrong value is produced (because of closure, other answers also highlight the issue).
I think I should say also that it's not issue of single function/mutliple functions—it's a question of working with closures. You see, I can implement a working example WITH many closures: https://jsfiddle.net/pr7gqtdr/1/. I do basically the same thing that you do in a single-function handler, but every time call the new 'helper' function:
nodes[i].onclick = function (i) {
return function (e) {
alert(i);
};
}(i);
See, this (i) at the end is an immediate function call, so onclick gets a function with i variable set in closure.
But, the single function options is a bit better, because it's more memory efficient, I guess. Functions are objects. If you create many of them, you take more memory, in general. So, choosing from these, I'd stick with 'handler' function option.
The bad example creates a lot of event handlers; One per event. The good example create a single event handler and assigns it to all the events.
With the bad example, you've created lots of separate functions, instead of just one. That can be a lot of extra overhead and a lot of potential scope problems. These include closure issues such as an event only firing for the last item in the loop.
Additionally, the good example allows you to more easily unsubscribe the events because you have access to the original function pointer.
The good example is also just easier to read and understand. The loop is only used for creating the elements and binding their events; The handling of those events is done elsewhere.
As Soviut mentions, you are creating lots of event handlers in the bad example. Moreover, it is important to point out that the bad example functions refer to the same i variable, which means all of them will have the last value of nodes.length when they execute.
This is because a closure is created. You can read more about it in Closures.

Global variable passed in a function not affected by it on the global scope [duplicate]

This question already has answers here:
Is JavaScript a pass-by-reference or pass-by-value language?
(33 answers)
Closed 6 years ago.
First of all, I'm sorry for asking this which was seemingly asked before, but...
In the questions I found, people talk about scope (and I'm familiar with the concept) or about some "callback" or "asynchronous" things that I don't really understand. The reason why I ask this is because all my global variables so far, were normally affected by the great number of functions.
The code I have is way too big for me to paste it here, but I'll give you an example.
var ContainsSomeNumber = 50;
var AnObjectWithSomeValues ...
var TheFunctionInQuestion = function(nameRepresentingTheObject, nameRepresentingNumberVar){
if(nameRepresentingTheObject.lestSayName ==== "SomeString"){
nameRepresentingNumberVar+=5;
}
}
Now the thing is, it understands both variables and gets the correct result when I pass them into the function, however, the ContainsSomeNumber variable remains unaffected by the function on a global scale. If I place an alert or log to show me how much it is inside the function, it shows 55, as it should. But once I try to access the modified global variable in another place with the modified value after the function has been run successfully (chrome and firefox show no foul play), it returns the same unmodified value of 50.
Is the problem because I didn't use hardcode versions of variables to pass in the function, or what? Because like I said, it sees and modifies it the right way, but only inside the function. Shouldn't scope see the variable as a global one as I didn't write "var" in front of the one I used in the function? I mean, I didn't declare it as a new variable, plus it took it's global value normally. Why doesn't the global variable stay modified by the function?
Javascript is always pass by value, but when a variable refers to an object (including arrays), the "value" is a reference to the object.
So if you want to a function modify an variable outside there scope, you should pass an object and change some attribute inside the object.
var obj = {
ContainsSomeNumber: 50;
}
var AnObjectWithSomeValues ...
var TheFunctionInQuestion = function(nameRepresentingTheObject, obj){
if(nameRepresentingTheObject.lestSayName === "SomeString"){
obj.ContainsSomeNumber+=5;
}
}
Or you can return the value:
var ContainsSomeNumber = 50;
var AnObjectWithSomeValues ...
var TheFunctionInQuestion = function(nameRepresentingTheObject, nameRepresentingNumberVar){
if(nameRepresentingTheObject.lestSayName === "SomeString"){
nameRepresentingNumberVar+5;
}
return nameRepresentingNumberVar
}

JS: this reference does not work [duplicate]

This question already has answers here:
Using this in event handler in strict javascript?
(3 answers)
Closed 7 years ago.
I want to encapsulate my Javascript code for certain objects in structures as indicated below. However, I ran into trouble with the semantics of this.
While this during tiles.init() refers to the tiles object, in the event handlers it refers to the event, i.e. I cannot use this to call other methods from my object.
Is there any way to pass the object to the event handlers, such that I do not have to use the global variable to call my own sub-routines and still retain this from the callback context?
I put up a JSFiddle here. This is the working JS part:
myData = {
'color': "red",
'makeRed': function(){
// don't want reference to global here!
$(this).css("color",myData.color);
},
'reset': function(){
$(this).css("color","");
},
'init': function(){
$("#click").hover(this.makeRed,this.reset);
}
};
myData.init();
I found several solutions like this and this idea to pass additional arguments. The question has been marked a duplicate of this, but using .bind() wastes the this required for the jQuery inside the callback.
Any idea how to get both tiles and this of the event context to the handler function without using globals?
You can use a closure variable in the init method
'init': function () {
var self = this;
$("#tiles div.tile").hover(function () {
self.hoverIn(this);
}, function () {
self.hoverOut(this);
});
}
Your construct is not working because this inside the hover callbacks does not refer to the tiles object

JavaScript not writing variable as number (loop) [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 9 years ago.
I have a javascript code like this and this always gives me a problem
for(var i=1;i<9;i++){
document.getElementById('element'+i).onclick=function(){
theFunc(i)
}
}
It selects the right element and adds the onclick. But, when I type in console document.getElementById('element1").onclick it returns theFunc(i) (not theFunc(1))
So no matter which element is clicked it will always call theFunc(9) (at the end i is 9)
What's wrong with my code?
Your event handler function has an enduring reference to i, not a copy of its value, as you've discovered.
To prevent that, have the function close over something else that won't change:
for(var i=1;i<9;i++){
document.getElementById('element'+i).onclick=makeHandler(i);
}
function makeHandler(index) {
return function() {
theFunc(index);
};
}
makeHandler creates a function that closes over index, which is a copy of the value of i, and so doesn't change as the loop continues. Each event handler gets its own index.
That said, creating a bunch of event handler functions that are effectively identical usually means you can redesign a bit and use just one handler function. In this case, for instance, you could do this:
for(var i=1;i<9;i++){
document.getElementById('element'+i).onclick=theHandler;
}
function theHandler() {
func(parseInt(this.id.replace(/\D/g, ''));
}
...which grabs the value to use from the id of the element.
Another approach is delegation, where you actually hook the click event on an ancestor element (one that all of these elementX's have in common), and then when the click occurs, look at event.target and its ancestors to see what you should do.
TJ Crowder's answer is the best way around your problem. This "problem" you're experiencing in your closure is by design in many languages, and is referred to as scope.
Here's a good explanation of different scopes in JavaScript (including closures) and how to use them.
http://robertnyman.com/2008/10/09/explaining-javascript-scope-and-closures/
When you say theFunc(i) you are creating a closure around i, such that every function call refers to the same variable. You need to wrap the function inside an outer closure to ensure each function call is working with a unique variable:
for(var i=1;i<9;i++){
(function(i){
document.getElementById('element'+i).onclick=function(){
theFunc(i);
}
})(i);
}

Why does this Javascript object not go out of scope after $(document).ready?

I have some working Javascript that manipulates the some DOM elements. The problem is, I don't understand why it works, which is never a good thing. I am trying to learn more about object oriented javascript and javascript best practices, so the organization may seems a little strange.
Basically, I wrap two methods that manipulate the DOM inside a CSContent object. I create an instance of that object, content in $(document).ready and bind some events to the functions in content. However, I am confused as to how these functions can still be called after $(document).ready exits. Doesn't that mean that content has gone out of scope, and its functions are not available? Anyway, here is the code:
function CSContent() {
var tweetTextArea = document.getElementById('cscontent-tweet'),
tweetTextElement = document.getElementById('edit-cscontent-cs-content-tweet'),
charCountElement = document.getElementById('cscontent-tweet-charactercount');
this.toggleTweetTextarea = function () {
$(tweetTextArea).slideToggle();
};
this.updateTweetCharacterCount = function () {
var numOfCharsLeft = 140 - tweetTextElement.value.length;
if (numOfCharsLeft < 0) {
$(charCountElement).addClass('cscontent-negative-chars-left');
}
else {
$(charCountElement).removeClass('cscontent-negative-chars-left');
}
charCountElement.innerHTML = '' + numOfCharsLeft + ' characters left.';
};
}
$(document).ready(function () {
var content = new CSContent();
//If the twitter box starts out unchecked, then hide the text area
if ($('#edit-cscontent-cs-content-twitter:checked').val() === undefined) {
$('#cscontent-tweet').hide();
}
$('#edit-cscontent-cs-content-twitter').change(content.toggleTweetTextarea);
//Seems wasteful, but we bind to keyup and keypress to fix some weird miscounting behavior when deleting characters.
$('#edit-cscontent-cs-content-tweet').keypress(content.updateTweetCharacterCount);
$('#edit-cscontent-cs-content-tweet').keyup(content.updateTweetCharacterCount);
content.updateTweetCharacterCount();
});
This, m'lord, is called a closure: the local variable content will remain in memory after $(document).ready exits. This is also a known cause of memory leaks.
In short, you bind this function to an event listener of a DOM element and then the JavaScript garbage collector knows that it should keep the local variable intact. You can't call it directly (outside of the function), unless the event is triggered. With some, you can do this ‘manually’, if you really want to call the function afterward (e.g., using element.click() to simulate a click).
I assume you wonder why the event handlers like
$('#edit-cscontent-cs-content-twitter').change(content.toggleTweetTextarea);
work?
Well you don't pass content as event handler but the function that is contained in content.toggleTweetTextarea. And this reference will still exist after content does not exist anymore. There is nothing special about it. You just assigned an object (the function) to another variable. As long as at least one reference to an object exists, the object won't be garbage collected.
Now you may ask why those functions have still access to e.g. tweetTextArea ? This is indeed a closure. When the functions are created via new CSContent(), the activation context of this function is added to the scope chain of the inner functions CSContent.toggleTweetTextarea and CSContent.updateTweetCharacterCount. So even if you don't have a reference to content anymore, the scope of this function is still contained in the scope chain of the other functions.
You won't be able to access the object contained in content anymore after ready() is finished, this indeed goes out of scope.
My brain is off today, but shouldn't you be using closures in this situation?
$('#edit-cscontent-cs-content-twitter').change(
function(){
content.toggleTweetTextarea();
}
);

Categories

Resources