6

I've been trying to learn javascript by refactoring some Jquery examples in a book into javascript. In the following code I add a click listener to a tab and make it change to active when the user clicks on the tab.

var tabs = document.querySelectorAll(".tabs a span");
var content = document.querySelectorAll("main .content li");
for (var tabNumber = 0; tabNumber <= 2; tabNumber++) {
 tabs[tabNumber].addEventListener("click", function (event) {
 
 for (var i = 0; i < tabs.length; i++) {
 tabs[i].classList.remove("active");
 }
 tabs[tabNumber].classList.add("active");
 for (var i = 0; i < content.length; i++) {
 content[i].innerHTML = "";
 }
 event.preventDefault();
 });
}

This returns an undefined error when I run it. However, I tried replacing tabs[tabNumber].classList.add("active") with this.classList.add("active") and it worked.

Why doesn't the previous code work? As far as I can see they are referring to the same thing, and tabs[tabNumber] should work since at that point in the code it is tabs[0].

Hatchet
5,4161 gold badge35 silver badges43 bronze badges
asked Mar 14, 2016 at 1:39
2
  • 3
    If you console.log(tabNumber) within your event handler you'll find it is 3, no matter which item you clicked. Commented Mar 14, 2016 at 1:44
  • use let for for loops as much as possible, but the issue can be resolved by closure or the place you reference it in your JS code. Commented Mar 14, 2016 at 1:55

2 Answers 2

4

If use this, I think it's better and a more polished solution. If you still want to use tabNumber, it's probably evaluating to 3 in every click callback, because it's the number after the last iteration, and you don't have a tabs[3] position.

So, you just have to make a closure of the tabNumber variable.

answered Mar 14, 2016 at 1:44
Sign up to request clarification or add additional context in comments.

Comments

3

I guess other answers told you why tabs[tabNumber] does not work (because it comes from the score of the for loop and so, is always equal to the greater value of tabNumber).

That's why I would recommend using a .forEach loop. Careful though because it doesn't work on arrays of DOM nodes produced by document.querySelectorAll(), but you can use:

// ES6
Array.from(document.querySelectorAll('...'))
// ES5
[].slice.call(document.querySelectorAll('...'))

Anyway, I made a simplified working demo of your code. Note that I save the currently active tab in a variable, to prevent another for loop. You could also do:

document.querySelector('.active').classList.remove('active')

But I like to reduce the amount of DOM reading.

Good luck for your apprentissage, re-writing some jQuery code into Vanilla JS seems like a good method, and you might acquire a far better comprehension of JavaScript.

answered Mar 14, 2016 at 2:14

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.