I'm not sure exactly how to describe what I want. I want to define a function with the parameters being a local VALUE not a reference.
say I have list of objects I want to create
for(i = 0; i < 10; i++){
var div = document.createElement("div");
div.onclick = function(){alert(i);};
document.appendChild(div);
}
Now I believe in this example no matter what div I click on, it would alert "10"; as that is the last value of the variable i;
Is there a way/how do I create a function with the parameters being the value they are at the time I specify the function... if that makes any sense.
You need to create the function inside another function.
For example:
div.onclick = (function(innerI) {
return function() { alert(innerI); }
})(i);
This code creates a function that takes a parameter and returns a function that uses the parameter. Since the parameter to the outer function is passed by value, it solves your problem.
It is usually clearer to make the outer function a separate, named function, like this:
function buildClickHandler(i) {
return function() { alert(i); };
}
for(i = 0; i < 10; i++){
var div = document.createElement("div");
div.onclick = buildClickHandler(i);
document.appendChild(div);
}
You could use an anonymous function:
for(i = 0; i < 10; i++){
(function(i){
var div = document.createElement("div");
div.onclick = function(){alert(i);};
document.appendChild(div);
})(i)
}
Related
var arr = []
for (var i = 0; i < 5; i++) {
arr[i] = function(id) {
return function() {
return id;
}
}(i);
}
for (var index in arr) {
console.log(arr[index]());
}
My thinking:
'I' which is in the (i); will refer to a global variable 'I'.
'I' which is in the (i); will be saved in the 'id' which is in the function(id).
'id' is a local variable of outer function. 'id' which is in the inner function will refer to 'id' which is a local variable of outer function. Then, the result is '0,1,2,3,4'.
But I think I can get the same result without outer function.
var arr = []
for (var i = 0; i < 5; i++) {
arr[i] = function(id) {
return id;
}(i);
}
for (var index in arr) {
console.log(arr[index]());
}
In this case, 'I' which is in the (i); will refer to 'I' which is a global variable.
'I' which is in the (i); will be saved in 'id' which is a local variable of function.
'id' which is behind return code will refer to 'id' which is a local variable of function. Then, the result is '0,1,2,3,4'.
Using outer function like the first code is unnecessary because the second code is possible. Am I right?
You could even not use any function at all, and just set arr[i] = i. But something tells me that you've simplified the supplied code in such a way that the reason for using such a pattern was removed.
This kind of function pattern is called partial function application, where you apply some parameters to a function and get back another function of smaller arity.
In such a case as what you've shown, one benefit of this approach is saving the execution of a result function to be called later, but applying the known parameters before that time.
So, I have a question. Inside a function I have a createElement() function that creates a li. I also use the setAttribute() function to add a onclick(). But here's my question, can i store like an array inside the onclick(), so i can get the array in a new functions argument? What should i put inside 'changeClass("' + '");') ? ("names" is a global variable that contains an array. "numberOfCookies" is nothing to worry about.)
function getClassCookie() {
var cookiesArray = document.cookie.split(';');
numberOfCookies = cookiesArray.length;
for (var i = 0; i < cookiesArray.length; i++) {
var nameValueArray = cookiesArray[i].split('=');
var split = nameValueArray[1].split(',');
var className = split[0];
var copySplit = split.slice();
copySplit.splice(0, 1);
names = copySplit;
var newLi = document.createElement('li');
var textNode = document.createTextNode(className);
newLi.setAttribute('onclick', 'changeClass("' + '");');
newLi.setAttribute('onclick', 'hideDropdown();');
newLi.appendChild(textNode);
var parent = document.getElementById('classes');
parent.appendChild(newLi);
}
}
function changeClass(c) {
names = c;
}
You can use the array in the handler with a closure, if you attach the handler properly with Javascript, such as with addEventListener. (Inline handlers are generally difficult to manage and aren't considered good practice, anyway). For example, once you've declared newLi, simply reference the array you want to call changeClass with later:
newLi.addEventListener('click', function() {
changeClass(copySplit);
});
The above code will allow the copySplit that was created in that iteration of the for loop to be the one that's referenced when the newLi is clicked later. (This technique isn't possible when you use inline handlers)
One more thing - variables declared with var are hoisted and have unintuitive function scope, rather than block scope. Make sure to use const or let instead, which have block scope (that way each iteration of the loop has separate bindings for variables, rather than all iterations sharing a single set of variables):
for (let i = 0; i < cookiesArray.length; i++) {
const nameValueArray = cookiesArray[i].split('=');
const split = nameValueArray[1].split(',');
const className = split[0];
const copySplit = split.slice();
// etc
Using string concatenation for the function name while attaching it to an attribute isn't really a good approach. Just use the function hook. If you need to pass parameters into it, then you can do that as well in an anonymous call.
In the example below, note that the demo array maintains its state on subsequent clicks because of its scope.
function handler(arr){
arr.push(arr.length);
console.log(arr);
}
(function demo(){
var newButton = document.createElement('button');
newButton.textContent = "Demo";
var someArray = [];
newButton.onclick = function(){
handler(someArray);
}
document.body.appendChild(newButton);
})()
I don't know how to ask correctly what I need, so let me try..
How can I pass a current variable content (literal) to a function dinamically created?
Maybe with this following code you understand better:
function MyClass(){
for (i = 1; i <= 10; i++){
this['show' + i] = function(){
alert('You called show' + i);
};
}
}
var obj = new MyClass();
obj.show3();
What I would like to be displayed in alert is "You called show3" instead of "show11".
Thanks
Since javascript doesn't have a block scope (until let in ECMAScript 6) your original function will be bound with the same value of i.
Calling another that generate itself a new function you avoid that and you give to i different values.
function MyClass() {
for (i = 1; i <= 10; i++) {
this['show' + i] = myFunc(i);
}
}
function myFunc(i) {
return function() {
alert(i);
}
}
var obj = new MyClass();
obj.show3();
There is a mechanism called binding in JavaScript. You can bind scope and any variables to the function in order to use them inside function. Scope will define this keyword inside function. All other binded variables will be available as arguments thus i variable in this example will have not a global but local scope and have a value passed in function creation.
for (i = 1; i <= 10; i++){
this['show' + i] = function(i){
alert('You called show' + i);
}.bind(this,i);
}
Take a look at the following code
//btns is an array passed as a parameter to a function
for(var i = 0, b; b = btns[i]; i++) {
b.handler = function () {
var a = btns[i].some_field; //undefined
//the same for "b.some_field;"
};
}
Why btns[i] is undefined?
PS. the code adds click handler on extjs buttons if that matters.
This happens because by the time the inner function is called (which is after the loop is done) the value of i would be btns.length and therefore the value of btns[i] would be undefined.
You need to close over the value of i like this:
b.handler = function(i) {
return function() {
var a = btns[i].some_field;
}
}(i);
It's important to note that although the variables have the same name, they're different variables; i.e. the inner variable shadows the outer, thereby "fixing" the value.
for(var i = 0, b; b = btns[i]; i++) {
b.handler = function () {
var a = this.btns[i].some_field;
//the same for "b.some_field;"
};
}
give "this." in side the function we have to use "this" to point
I have the following code:
for(var i = 0; i < nodelist.length; i++) {
var x = functionThatCreatesADivElement();
someElement.appendChild(x.getDiv()); // this works fine
nodelist[i].onclick = function() {
x.someFunction(); // this always refer to the last 'x' object
}
}
function functionThatCreatesADivElement() {
var div = document.createElement("div");
this.someFunction = function() {}
this.getDiv = function() {
return div;
}
return this;
}
the problem is that the execution of nodelist[0].onclick is exactly the same as nodelist[4].onclick (assuming that i = 4 is the last node).
I believe the references of the previously iterated are changing to point to the currently iterated element.
What is the proper way of doing this?
EDIT: Added some more code and changed the name of the function cause it was too confusing
You have two problems. The first problem is that JavaScript variables don't have block scopes.
From MDN:
When you declare a variable outside of any function, it is called a global variable, because it is available to any other code in the current document. When you declare a variable
within a function, it is called a local variable, because it is available only within that
function.
JavaScript does not have block statement scope;
You aren't enclosing a the x variable in a function, so all of your onclick callbacks are using the same x variable, which point to whatever element is last in the loop since that will be the last one to have overwritten x.
Doing this for your loop should work:
nodelist.forEach(function (nodeitem) {
var x = functionThatCreatesADivElement();
someElement.appendChild(x.getDiv());
nodeitem.onclick = function() {
x.someFunction();
}
});
The second problem is that your functionThatCreatesADivElement() constructor function is not being called correctly. Use new functionThatCreatesADivElement() since you are invoking a constructor function.
Solved. I had to use
var x = new functionThatCreatesADivElement();
function functionThatCreatesADivElement() {
var div = document.createElement("div");
this.someFunction = function() {}
this.getDiv = function() {
return div;
}
//return this; //Using new instead of returning this
}