Loop and Functions - javascript

I'm having troubles gathering information about clicked eventListeners.
I have this loop which builds an array:
myButtonList = document.getElementsByTagName('a');
myAnchorList = [];
for (i=0; i < myButtonList.length;i++) {
if (myButtonList[i].getAttribute('class') == 'flagged') {
myAnchorList.push(myButtonList[i]);
}
}
For each <a> put into myAnchorList array, I also create another array storing other informations from the same tag (classe and other atrributes).
Here's where I'm struggling. I'm trying to set up an eventListener to send me back those information when those <a> are being clicked. But somehow, the fact that I create a function (for the eventListener) within a loop breaks everything.
for (i=0; i < myAnchorList.length; i++) {
myAnchorList[i].addEventListener("click", function(i){
console.log(alpha+' - '+beta[i]+" - "+charlie[i]);
});
}
My values will either be undefined or some other values which will be the same for each buttons I clicked. alpha is working well as it doesn't depend on any iteration of the loop, but not the others.
Can anybody see what I'm doing wrong here?

for (var i = 0; i < myAnchorList.length; i++) {
(function (i) { //Passes i to your function
myAnchorList[i].addEventListener("click", function () {
console.log(alpha+' - '+beta[i]+" - "+charlie[i]);
});
})(i);
}

The variable "i" in closure that you created in the loop will always retrieve the last value(myAnchorList.length - 1). You shouldn't create closure in a loop, and you can use a "factory" method to create closure instead.

Related

scroll function inside a loop not working because of undefined property

I want to change the style of items while scrolling.
My code is working if I target the ID, but I have to target many items.
So I changed it for class name and add a "for" loop to get through every items.
It ended with the error "Cannot read property 'style' of undefined".
Can someone explain me where I am wrong ?
var gear = document.getElementsByClassName("rotate-block");
for (var i = 0; i < gear.length; i++) {
window.addEventListener("scroll", function() {
gear[i].style.transform = "rotate("+window.pageYOffset/2+"deg)";
});
};
Your code is using a closure-based access to i inside the scroll listeners.
Because you defined your index using var rather than let, all these closures reference the same i, which is evaluated when the listener is executed, not when it is defined.
After your last iteration of the for-loop, i is equal to gear.length, which means any of the listeners is trying to access gear[gear.length]. The highest index available on any array is length - 1 though.
To fix your issue, simply switch from
for (var i = 0; i < gear.length; i++)
to
for (let i = 0; i < gear.length; i++)
So this is the basis of the error you are describing...
...but
Why are you adding more than one scroll listener in the first place?
You probably instead want to iterate over gear inside the listener, at which point using var is perfectly fine since it's no longer accessed as a closure.
var gear = document.getElementsByClassName("rotate-block");
window.addEventListener("scroll", function() {
for (var i = 0; i < gear.length; i++) {
gear[i].style.transform = "rotate("+window.pageYOffset/2+"deg)";
}
});
For the future, I highly recommend to switch to using for...of to iterate over iterables:
window.addEventListener("scroll", function() {
for (const gear of document.getElementsByClassName("rotate-block")) {
gear.style.transform = "rotate("+window.pageYOffset/2+"deg)";
}
});

JS multidimensional array spacefield

i wanna generate a 3x3 field. I want to do this with JS, it shall be a web application.
All fields shall inital with false. But it seems so that my code is not working correctly, but i don't find my fault. The goal is, that every spacesector is accessible.
Thats my idea:
// define size
var esize = generateSpace(3);
}
space[i] = false is replacing the array with a single boolean value false, not filling in all the entries in array you just created. You need another loop to initialize all the elements of the array.
function generateSpace(x) {
var space = [];
for (var i = 0; i < x; i++) {
space[i] = [];
for (var j = 0; j < x; j++) {
space[i][j] = false;
}
}
return space;
}
Also, your for() loop condition was wrong, as you weren't initializing the last element of space. It should have been i < space.length.
And when it's done, it needs to return the array that it created.
Since I got somewhat bored and felt like messing around, you can also initialize your dataset as shown below:
function generateSpace(x) {
return Array.apply(null, Array(x)).map(function() {
return Array.apply(null, Array(x)).map(function() {
return false;
});
});
}
The other functions work equally well, but here's a fairly simply looking one using ES6 that works for any square grid:
function generateSpace(x) {
return Array(x).fill(Array(x).fill(false));
}

Creating an array and storing it in sessionStorage with JavaScript?

I'm doing an assignment that requires us to add objects to a fake cart array from a fake database array, then go to a cart page that displays everything in the "cart." Now, that's all well and good, but for some reason I can't get more than one object to show up in the fakeCart array.
I'm fairly certain the issue is in this function, because everything displays properly otherwise in every way.
So, it turns out I posted code that I was tinkering with. I've since updated it to the almost-working one.
function addToCart(e) {
'use strict';
var fakeCart = [];
for (var i = 0; i < fakeDatabase.length; i++) {
if (fakeDatabase[i].id == e.currentTarget.id) {
fakeCart.push(fakeDatabase[i]);
}
}
sessionStorage.fakeCart = JSON.stringify(fakeCart);
}
Essentially, I can get the code to make a single object go from one array (database) to the other (cart), but whenever I try to add one back in it just replaces the last one.
The code overwrites any existing value of sessionStorage.fakeCart, so there will never be more than one element in the serialized array. You can fix that by reading the value from sessionStorage instead of creating a new list each time.
function addToCart(e, productNum) {
'use strict';
// change this
var fakeCart = JSON.parse(sessionStorage.fakeCart) || [];
for (var i = 0; i < fakeDatabase.length; i++) {
if (fakeDatabase[i].id == e.currentTarget.id) {
// and this
fakeCart.push(fakeDatabase[i]);
}
}
sessionStorage.fakeCart = JSON.stringify(fakeCart);
}
I think :
Instead of
fakeCart[i].push(fakeDatabase[i]);
you require this
fakeCart.splice(i, 0, fakeDatabase[i]);

How do I correctly use an iteration value in JavaScript?

I am creating a 'simple' javaScript function which basically displays new information on the page when a user clicks next or previous.
The information is taken from an array and I want to use either i++ or i-- to call an element of the array.
Heres my JavaScript:
var titles = ["Dundalk", "Navan", "Drogheda", "Dublin"];
var i = 0;
function next()
{
i++;
if (i == titles.length)
{
i = 0;
}
var object = document.getElementById('tname');
object.innerHTML = titles[i];
}
function prev()
{
if (i == 0)
{
i = titles.length;
}
i--;
var object = document.getElementById('tname');
object.innerHTML = titles[i];
}
The problem is, when I run this code in my HTML page, I get an 'UNDEFINED' result. The JavaScript is not recognizing that i has been initialized as 0 in the beginning.
If i change titles[i] to titles[2], for example, the correct text is displayed in HTML.
What am I forgetting or how can I overcome this?
Thanks
The fact that you're seeing undefined indicates that you're accessing an array index which hasn't been set. Your code looks fine at a glance, so I would guess that there's some more code you're not showing which also uses i as a loop variable and leaves it set to a value > titles.length after the code above has run.

JavaScript DOM references not holding up

For some reason ss.transition() does not affect the appropriate DOM elements after ss.goTo() is triggered by an onclick. The ss.transition() call under //Init does work as expected. I assume this is a scope problem. Little help?
var ss = {};
ss.goTo = function(i) {
ss.old = ss.current;
ss.current = ss.slides[i];
ss.transition();
}
ss.transition = function() {
ss.old.style.display = "none";
ss.current.style.display = "block";
}
// Hooks
ss.div = document.getElementById("slides");
ss.as = ss.div.getElementsByTagName("a");
// References
ss.slides = [];
for (i in ss.as) {
if (ss.as[i].rel == "slide") {
ss.slides.push(ss.as[i]);
}
}
ss.first = ss.slides[0];
ss.last = ss.slides[ss.slides.length-1];
// Init
ss.current = ss.first;
ss.old = ss.last;
ss.transition();
for (i in ss.as) {
You shouldn't use the for...in loop over an Array or, in this case, NodeList. You'll get member properties you don't want, like item and length. You also can't rely on the items being returned in any particular order; it is very likely that at least ss.last will not be what you expect. If it's a non-item property, ss.old.style.display will definitely fail with an exception, breaking the script.
The correct loop for a sequence is the old-school C construct:
for (var i= 0; i<ss.as.length; i++)
Also, where are you binding the calls to goTo? If you are doing it in a loop with a function inside you, you may well also have the classic loop closure problem. See eg. this question.
The reason for the failure is because you lose the reference to the currently hidden element before you make it show up again. You need to assign old to display:block, then do the switch of old = current, current = variable, then hide old.

Categories