Javascript assigning value to variable - javascript

I am trying to make a cell in a table tell me its number when I click it.
for(var j = 0; j < 8; j++){
var cell = row.insertCell(j);
cell.name = j;
cell.onclick=function(){alert(cell.name)};
}
This however, prints the number 8 for every cell. How do I save the value of j in cell.name, instead of just having it point to the variable j?
Thanks.

IMPORTANT: All JavaScript developers should know this. It will cause all kinds of weird bugs that is very hard to find.
It is a common mistake of people who are new to JavaScript. I've made the same mistake before.
A function inside a loop is NOT created for every iteration. It is the same one function object with the same closure scope. Thus, your cells will have the exact same onclick callback.
My advice here is NEVER EVER create a function inside of loop. Instead, create and call a function that returns a callback function and assign it to onclick.
for (var j = 0; j < 8; j++) {
var cell = row.insertCell(j);
cell.name = j;
cell.onclick = createOnClick(cell);
}
function createOnClick(cell) {
return function () {
// do whatever you want to do with cell
};
}

Related

How to add onclick functions in an array of buttons using a for loop properly?

I'm making a kind of HTML calculator to test something I have in mind.
I've used a for loop to create the buttons of the keypad. The display is a text field.
Then I used a for loop to add the functions in the buttons:
for (var i = 0; i < 10; i++)
{
buttons[i].onclick = function()
{
display.value += i;
};
}
What I was trying to do is to make, for example, buttons[0] add "0" to the value of the text field when clicked. Instead, clicking any button added "10" in the text field. Why? How can I make it right?
You almost got it right , you just need to change var to let in your loop declaration :
for (let i = 0; i < 10; i++)
{
buttons[i].onclick = function()
{
display.value += i;
};
}
What's the difference between using "let" and "var"? Here you can get more info about your issue.
Your problem is that you are referencing i directly in your functions that you are binding to your Buttons. i will actually continue to exist even after you bound all your events, and its value will be the last value of the iteration 10. So whenever a click function runs, it looks up i and finds the last value you set (10) and takes that value. What you want to do is add a constant reference instead - so that you bind that value you have during the loop and keep that reference forever, no matter how i might change later.
for (var i = 0; i < 3; i++) {
const localValue = i
buttons[i].onclick = function()
{
counter += localValue;
counterElement.innerHTML = counter
};
}
I created a small example fiddle here: https://jsfiddle.net/4k8cds9n/ if you run this you should see the buttons in action. Some related reading for this topic would be around scopes in javascript, one good article: https://scotch.io/tutorials/understanding-scope-in-javascript

What happens if you put an iteration count of a for loop as a variable?

I want to make a program in javascript in which a person inputted the iteration count for a for loop(they could input x++, or y--), but I don't know if I am using the right method.
Here is my code:
var x = prompt("iteration count")
// x should equal something like, i++, or x--
for(var i = 0; i < 10; x){
document.write(i)
}
But when the code ran the program kept crashing.
Why is it crashing and how do I fix this?
Please Help
you need to parse the int value of x because it's a string and use it to increment i
var x = parseInt(prompt("iteration count"))
for (var i = 0; i < 10; i += x) {
document.write(i)
}
EDIT :
based on the question edit and the comments, you can use eval(), but :
Do not ever use eval!
eval() is a dangerous function, which executes the code it's passed with the privileges of the caller.
So before you use it, read the MDN page and check : eval isnt evil it's just misunderstood
where there's this comment from Spudley :
From a security perspective, eval() is far more dangerous in a server
environment, where code is expected to be fully trusted and hidden
from the end user.
In a browser, the user could eval any code they wanted at any time
simply by opening dev tools, so as a developer you can't get away with
having anything on your client code that could be insecure against
eval anyway.
to test the snippet below, type i++ in the prompt
var x = prompt("iteration count");
for (var i = 0; i < 10; eval(x)) {
console.log(i)
}
an alternative to eval() would be new Function or check the answers here : Programatically setting third statement of for loop
var input = 'i++';//Or whatever condition user passing in
var conditionProgramatically = () => new Function(input)() ;
for (var i = 0; i < 10; conditionProgramatically()) {
console.log(i)
}
For for-loop, third statement will be invoked/executed on every iteration, and hence we set a function call, and in that function, we execute whatever user passing in as you've mentioned i++
That is an endless loop because the variable i never incremented. Try this one.
var x = prompt("iteration count")
for(var i = 0; i < x, i++){
document.write(i)
}
You forgot to increment the index variable, it result to endless loop and maximum stack error, you can also use + for parseInt shorcut.
var x = +prompt("iteration count")
for(var i = 0; i < x;i++){
document.write(i)
}
You have to parse the input value and then make it as a condition to stop iterating after the given value.
var x = parseInt(prompt("iteration count"))
for (var i = 0; i < x; i++) {
document.write(i);
}

JavaScript variables declaration that are used in functions

I am new to programming and sorry if a question is dumb.
I am using several functions with variables i, j for looping like - for( j = 0; j < pol.length; j++) ...
Now my lack of knowledge is - I think that declaring "var i = 0..." in every function will creates a memory problem because these functions are used many times and it will declare "i" or "j" each time. So what I do now is I declare var i,j = 0; once outside the functions as a global and then just use them in functions without "var". But I have a feeling - this is terribly wrong.
Another variables that I declare as global "var" are the ones I use later in some functions. For example - var pol_owner = ""; So the code will play with these variables later and change their values. And now I have something like this at the start of my document:
var thePol = 0;
var nondrag = 0;
var intGl = 0;
var intMn = 0;
var oldGl = 0;
var oldMn = 0;
var money = 0;
var gifts = 0;
var g_spent = 0;
var the_mess = "";
var j = 0;
var x = 0;
var y = 0;
var i = 0;
And later my functions just use these variables without "var".
I know though the code runs well still I am doing it wrong. Can you give me any advice of how to properly do it. I was looking for some information about it but still I am not sure what is the proper way of doing it.
From the start I was doing my code as: for( var j = 0; j < pol.length; j++) not declaring "j" outside the function, but later I thought that every-time this function run it will declare this var again and again and will not it be more effective to declare it once from the start and later use it without "var". The only problem I see is - creating a global variable that I can accidentally use wrong in some part of the code. But I am kind of controlling this.
So the question basically is - is declaring "var i=0" in every loop part of the function creates any memory problem and should I declare this var once outside the function and use it later without declaration to avoid this problem?
Thank you in advance.

Javascript create a list of functions dynamically

I have a piece of JavaScript code that I want to create a list of functions. All the functions will be put in a dictionary d. d["a"] will give me the function function() {console.log("a")} and d["b"] will give me the function function() {console.log("b")} etc. This is my code:
var a = "abcdefghijklmnopqrstuvwxyz1234567890".split("");
var d = {};
for(var l = a.length, i = 0; i < l; i++)
{
d[a[i]] = function(){console.log(a[i])};
}
However, when I run the above code, d["a"] and d["b"] will be the same, they all point to function(){console.log(a[i])}. How to get what I want?
Thanks.
You need to give each instance of the function its own variable:
for(var l = a.length, i = 0; i < l; i++)
{
(function (x) {
d[a[x]] = function(){console.log(a[x])};
})(i)
}
They don't point to the same instance of function(){console.log(a[i])}, instead, you've created a bunch of functions that all use the same reference to i. The value that i points at changes as the for loop executes.
The other answers provided will work, but it involves generating twice as many functions as you need.
function makeLogFunction(whatToLog) {
return function() {
console.log(whatToLog);
}
}
var a = "abcdefghijklmnopqrstuvwxyz1234567890";
var d = {};
for(var l = a.length, i = 0; i < l; i++) {
d[a[i]] = makeLogFunction(a[i]);
}
Here, I have a makeLogFunction that will return a new function that always prints whatToLog. The other answers will generate a new "version" of the makeLogFunction every time the loop executes. For very large sets of data, this is a waste of time and memory.
This approach has added advantages of clarity and reusability. If there's a significant amount of logic happening in your loop, encapsulating it in a named function allows future reviewers to get a sense of what's going on by the name you give to the function. You can also reuse the function in other parts of your application.

Javascript 'this' as parameter

I'm trying to copy the value from "FROM" fields into "TO" fields. My first attempt was this:
function updateToField(toField,fromField)
{
toField.value = fromField.value}
}
function verifyFromToFields()
{
var inputs = getElementsByTagName("input");
for (var j = 0; j < inputs.length; j++)
{
if (inputs[j].name.indexOf('FROM') != -1 && if (inputs[j+1].name.indexOf('TO') != -1)
{
var fromField = inputs[j];
var toField = inputs[j+1];
fromField.onchange = function(){updateToField(toField,fromField)};
}
}
The website has several FROM-TO pairs, and this only seem to work for the last pair in the "inputs" array.
Then I tried this:
function updateToField(toField,fromField)
{
toField.value = fromField.value}
}
function verifyFromToFields()
{
var inputs = getElementsByTagName("input");
for (var j = 0; j < inputs.length; j++)
{
if (inputs[j].name.indexOf('FROM') != -1 && if (inputs[j+1].name.indexOf('TO') != -1)
{
var fromField = inputs[j];
var toField = inputs[j+1];
fromField.onchange = function(){updateToField(toField,this)};
}
}
With this, when any FROM field in the page is modified, it's copied to the last TO field on the page. I think this is one of those issues I've read about parameters as value or reference, but I can't figure it out.
Also this is a VERY simplified version of the code, I actually fill the inputs list with a getElementsByClass function and must search through childnodes.
Does anyone have a clue on whats going on?
That closure, I do not think it means what you think it means.
This line here:
fromField.onchange = function(){updateToField(toField,this)};
means "assign to onchange a function that assigns the contents of that fields to whatever toField is at the time of the change!
Since you only have one variable toField all the changeable fields will be assigned to it.
This would work:
var setOnChange = function(fromField, toField) {
fromField.onchange = function(){updateToField(toField,this)};
};
for (var j = 0; j < inputs.length; j++)
{
if (inputs[j].name.indexOf('FROM') != -1 && if (inputs[j+1].name.indexOf('TO') != -1)
{
setOnChange(inputs[j], inputs[j+1]);
}
}
EDIT: Isaac might have a better explanation of the problem (although I don't really like his solution).
That's is because of how closures work. When you assign the onchange function you create (for every loop) a new anonymous function that calls updateToField, but the value of the toField and this parameters (or any other you are passing) are not bind to the "current" loop, instead that parameters are bind to the last value of the loop (that's why is only working with your last "TO").
Instead of assigning a new function to onchange property try calling the Function.bind if you're running in an environment that has that or write one if you don't have it.
Here are the documentation for bind
So you can go like this:
fromField.onchange = updateToField.bind(this, fromField, toField);
Or you can use the other approach that Malvolio wrote.
The problem actually has to do with scope. What's happening is that your functions (the ones you assigned to onchange) are capturing the variables toField and fromField and their values keep changing. I know it looks like you've declared them anew each time through the loop, but that's not how JS works; consecutive trips through a loop share a scope, so fromField is the same variable each time through and you're merely assigning it a new value in each iteration. So at the end, all of your functions refer to the same fromField variable. And that fromField variable, naturally, contains the last value you assigned it.
So when you eventually call all of those functions, they all do the same thing, because all of their fromFields (and, by the same logic, toFields) are the same variable. So that explains why only the last inputs worked; they're what fromField and toField contained when you ran the functions.
You can fix this by introducing an intermediate function, since functions do create new scopes. That way each time through the loop, you get brand new variables.
function updateToField(toField,fromField)
{
toField.value = fromField.value;
}
function verifyFromToFields()
{
var inputs = getElementsByTagName("input");
for (var j = 0; j < inputs.length; j++)
{
function(){
if (inputs[j].name.indexOf('FROM') != -1 && if (inputs[j+1].name.indexOf('TO') != -1)
{
var fromField = inputs[j];
var toField = inputs[j+1];
fromField.onchange = function(){updateToField(toField,fromField)};
}
}();
}

Categories

Resources