i have this code and this is working:
var wrap_inner = document.getElementsByClassName("wrapper"),
base_icon = document.getElementsByClassName("inner");
function fx_effect () {
for (var i = 0; i < wrap_inner.length; i++) {
(function (index) {
wrap_inner[i].addEventListener('click',function() {
base_icon[index].classList.toggle('tc');
});
})(i); <==== what is that? what do that?
}
}fx_effect();
but i don't know what is the index in: function (index) { ...
and what is (i) at the end of function.
Why index is used? What is its value?
why (i) at the end of function used like this?
When should be used in this way?
why the function like this don't work???:
function fx_effect () {
for (var i = 0; i < wrap_inner.length; i++) {
wrap_inner[i].addEventListener('click',function() {
base_icon[i].classList.toggle('tc');
});
}
}fx_effect();
I'm confused :(
The concept is javascript closures . First method is creating a closure ( a scope or a stack as analogy) for each value passed. So that when fx_effect is called, the inner method runs for values 0 to wran_inner.length distinctively as each value is in a different closure.
For second method, the scope will have just the final value, hence calling the method fx_effect will result in the loop running that method for final value of i, i.e. wrap_inner.length - 1
Related
I am triying to create a constructor function in order to aferwards be able to instance it to create game boards objects. The program is simple, I just passed the constructor the number of rows and it has to create them.
The for loop is not working and I dont understand the reason, ySize variable is defined. If I remove the for the program works sucessfully and create the section with no problem. Which is the problem with the for loop?
Thank you very much for your time
function AlgorithmType(ySize) {
this.ySize = ySize;
this.createBoard = function() {
let selector; let row
for(let i = 0; i ++; i <= this.ySize) {
debugger; // Debugger is not executed
selector = document.querySelector(".mainContainer")
row = document.createElement("section")
selector.appendChild(row)
}
}
}
let testBoard = new AlgorithmType(5)
testBoard.createBoard() //expected to create 5 rows
Your function rebinds this, which is why this.ySize is not defined. Use an arrow function to access this of the lexical scope (parent scope):
function AlgorithmType(ySize) {
this.ySize = ySize;
this.createBoard = () => {
let selector; let row
for(let i = 0; i <= this.ySize; i++) {
debugger; // Debugger is not executed
selector = document.querySelector(".mainContainer")
row = document.createElement("section")
selector.appendChild(row)
}
}
}
Side note: As #atomNULL pointed out, your for-loop syntax is incorrect as well. See his answer
Your for loop syntax is wrong, the correct syntax would be:
for (let i = 0; i <= this.ySize; i++)
Also you should use an arrow function to access this instead of rebinding it current way.
I try to use jquery(jquery-2.1.1) and constructor function to build html. But when I tried this method in for loop, the loop can't stop. Can anyone tell me why it happens?
Here' s the code. Thanks a lot.
function tag() {
this.addClass = function(...theArgs) {
for (index in theArgs) {
this.$html.addClass(theArgs[index]);
}
}
this.setAttr = function(attr, value) {
this.$html.attr(attr, value);
}
this.append = function(...theArgs) {
for (index in theArgs) {
this.$html.append(theArgs[index]);
}
}
this.find = function(value) {
return this.$html.find(value);
}
this.empty = function() {
this.$html.empty();
}
this.remove = function(value) {
this.find(value).remove();
}
this.clone = function() {
return jQuery.extend(true, {}, this);
}
this.show = function() {
return this.$html[0];
}
}
function label(text) {
tag.call(this);
this.$html = $("<label></label>");
this.append(text);
}
for(var index = 0; index < 2; index++) {
var fieldLabel = new label(1);
console.log(index);
}
The problem here is you use the index (without var) as the running variable for all loops in your tag function. That index variable is still effective in the outer scope of the for-loop at the end (which should stop with the condition >=2).
At the beginning of the loop, index is 0. The next loop it should be 1. But when going into the inner append method, it's reset back to 0 due to the loop for-in (the argument passed in is just 1, so the spread notation makes an array of 1 length, and for-in stops with index set to 0) . So at the end of the second loop it is still 0. That means it will never become greater the value 1 (which is increased only at the beginning of the for-loop). The condition < 2 will always be satisfied and the for-loop just runs forever.
You can either use another name for the running variable in for-in. Or just declare another local-scoped index by using var, like this:
this.append = function(...theArgs) {
for (var index in theArgs) {
this.$html.append(theArgs[index]);
}
}
Or better using for-of as someone suggested.
I'm adding an event listener to some elements I'm looping through and need a closure in order to preserve the index in the event function.
<button>solution 1</button>
<button>solution 2</button>
<script>
var buttons = document.getElementsByTagName('button');
for (var i = 0; i < 3; i++) {
var log = (function closure(number) {
return function () {
console.log(number);
};
})(i);
buttons[0].addEventListener("click", log);
}
for (var i = 0, len = 3; i < len; i++) {
(function (i) {
var log = function () {
console.log(i);
};
buttons[1].addEventListener("click", log);
})(i);
}
</script>
http://jsfiddle.net/paptd/11/
Both these solutions output 0, 1, 2 correctly (try 'wrong' to see what happens without a closure) but I'm trying to understand which one I should use and why.
Which way is the correct way of doing it?
The first one works because you are defining a closure, returning a function from it, then assigning that function to a listener.
The second one seems more proper, since the closure encompasses the entire loop content, making it more obvious that the value of i is to be "locked" there.
You shouldn't use any of these--you're creating n identical functions inside of your loop. You should refactor your code into a named function that returns the event handler:
var buttons = document.getElementsByTagName('button');
function createHandler(number) {
return function () {
console.log(number);
};
}
for (var i = 0; i < 3; i++) {
buttons[0].addEventListener("click", createHandler(i));
}
Example: http://jsfiddle.net/paptd/12/
What is the difference between these functions? Why does the first one work and the second does not work as expected?
http://jsfiddle.net/GKDev/x6pyg/ (this works)
http://jsfiddle.net/GKDev/bv4em/ (and this is not)
I'm trying to loop over input elements and add onfocus events on them:
for (var i = 0; i < helpText.length; i++) {
var item = helpText[i];
document.getElementById(item.id).onfocus = function() {
showHelp(item.help);
};
}
In your non-working example, when the anonymous function is called, item has the last value it held when the for loop finished executing. That's because this variable belongs to the parent function that contains the for loop.
In your working example, you create a new function, pass in the current value of item like so:
function (help) {
return function () {
showHelp(help); // <-- This will be the value enclosed in this anonymous function
};
}(item.help); // <-- Calls an anonymous function passing in the current value
This creates a new closure around that value as it existed during that iteration. When the anonymous function is called, it uses that local value.
It is treated as:
var item;
for (var i = 0; i < helpText.length; i++) {
item = helpText[i];
document.getElementById(item.id).onfocus = function() {
showHelp(item.help);
};
}
The loop has finished before any focus callback fires and at that point item is the last item assigned.
It is pretty easy in fact.
In the first case you are passing item.help inside of the closure, which acts as a local copy. It's like a prisoner of its scope. What happens outside, nobody cares.
for (var i = 0; i < helpText.length; i++) {
var item = helpText[i];
document.getElementById(item.id).onfocus = function (help) {
return function () {
showHelp(help);
};
}(item.help);
}
In the second one, there is no closure preserving the value of item, which means that when item is evaluated, it evaluates to its actual value, i.e.: the last element of the array, since the last loop of the for set its value to the last value of the array.
for (var i = 0; i < helpText.length; i++) {
var item = helpText[i];
document.getElementById(item.id).onfocus = function() {
showHelp(item.help);
}
}
I have the following code that adds an onmouseover event to a bullet onload
for (var i = 0; i <= 3; i++) {
document.getElementById('menu').getElementsByTagName('li')[i].onmouseover = function () { addBarOnHover(i); };
}
This is the function that it is calling. It is supposed to add a css class to the menu item as the mouse goes over it.
function addBarOnHover(node) {
document.getElementById('menu').getElementsByTagName('li')[node].className = "current_page_item"; }
When the function is called, I keep getting the error:
"document.getElementById("menu").getElementsByTagName("li")[node] is
undefined"
The thing that is stumping me is I added an alert(node) statement to the addBarOnHover function to see what the value of the parameter was. The alert said the value of the parameter being passed was 4. I'm not sure how this could happen with the loop I have set up.
Any help would be much appreciated.
This is a common problem when you close over an iteration variable. Wrap the for body in an extra method to capture the value of the iteration variable:
for (var i = 0; i <= 3; i++) {
(function(i){ //here
document.getElementById('menu').getElementsByTagName('li')[i].onmouseover = function () { addBarOnHover(i); };
})(i); //here
}
an anonymous function is created each time the loop is entered, and it is passed the current value of the iteration variable. i inside the anonymous function refers to the argument of this function, rather than the i in the outer scope.
You could also rename the inner variable for clarity:
for(var i=0; i<=3; i++){
(function(ii){
//use ii as i
})(i)
}
Without capturing the iteration variable, the value of i when it is finally used in the anonymous handler has been already changed to 4. There's only one i in the outer scope, shared between all instances of the handler. If you capture the value by an anonymous function, then the argument to that function is used instead.
i is being passed as a reference (not by value), so once the onmouseover callback is called, the value of i has already become 4.
You'll have to create your callback function using another function:
var menu = document.getElementById('menu');
var items = menu.getElementsByTagName('li');
for (var i = 0; i <= 3; i++) {
items[i].onmouseover = (function(i) {
return function() {
addBarOnHover(i);
};
})(i);
}
You could make it a little more readable by making a helper function:
var createCallback = function(i) {
return function() {
addBarOnHover(i);
};
};
for (var i = 0; i <= 3; i++) {
items[i].onmouseover = createCallback(i);
}