2

I'm am battling with a javascript function I'm working on.

Inside a for loop I'm iterating all elements with class "visible", inside that loop I'm preforming two actions.

  1. elements[i].removeAttribute("class");
  2. elements[i].setAttribute("class", "hidden");

For some reason only 1 is valid. 2 produces an error saying:

Uncaught TypeError: Cannot call method 'setAttribute' of undefined

Even when I log elements[i] using console.log; after the first console.log call the element exists, but on the second console.log elements[i] is 'undefined'

What the hell am I missing here, this is driving me crazy, if my laptop wasn't so expensive it would have been broken by now. Help :(

Here's the function:

function hide_visable_elements()
{
    // remove body EventListener
    var body = document.getElementsByTagName("body");
    body[0].removeEventListener("click", hide_visable_elements, true);

    var elements = document.getElementsByClassName("visible");

    for (var i = 0; i < elements.length; i++)
    {
        console.log(elements[i]); // Works like a swiss clock

        elements[i].removeAttribute("class"); 

        console.log(elements[i]); // why elements[i] is 'undefined' now ???

        elements[i].setAttribute("class", "hidden"); // << turns to useless code
    }
}
4
  • 1
    Just a guess. When you removed 'class', the element drops out of the array, because it no longer fits the criteria. Have you thought about using jQuery? It will makes things easier. Commented Jul 23, 2013 at 10:31
  • 1
    If you have removed the class from the element, you have also removed it from the set of elements that has that class. Basically, you erased that element. Commented Jul 23, 2013 at 10:31
  • simpler if you use .removeClass() of Jquery. Commented Jul 23, 2013 at 10:34
  • @captain I need to have a good understanding of javascript prior to using frameworks :) Commented Jul 23, 2013 at 10:39

3 Answers 3

6

This is because getElementsByClassName returns a NodeList, which is live. That is, it updates itself when the elements it refers to change.

When you remove the class attribute from an element in the NodeList, it gets removed from that list (since it no longer has the visible class name).

You don't actually need to remove the attribute. Just setting it will do the job just as well. But since the NodeList is changing as you manipulate the elements it contains, you need to count backwards through it (as each time you change one element of it, it is removed so the length decreases by one):

for (var i = elements.length - 1; i >= 0; i--) {
    elements[i].setAttribute("class", "hidden");
}
Sign up to request clarification or add additional context in comments.

1 Comment

The two actions are mandatory in order the achieve the css transition. If that's what you meant.
1

getElementsByClassName is a live NodeList so changing className of the items immediately affects whole list. I would recommend use querySelectorAll insead.

Plus instead of var body = document.getElementsByTagName("body"); use document.body.

3 Comments

Can you provide the example of querySelectorAll again? I'm learning as I go.
Thanks. btw why should i use document.body ? performance ? Edit: using document.body doesn't find any body tags.
document.body is convenient shortcut. Why select it if the reference to it is already available?
1

I think that the problem is elements[i].removeAttribute("class"); since you selected the element using a class getElementsByClassName("visible"); . I think so when you remove class attribute completely from the element things are going wrong.

Try some tweak with the code. You are not suppose to remove attribute class if you are planning to use the same element which is selected using class attribute.

Comments

Your Answer

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.