0

I'm trying to change the class of a div using the onclick notifier of an object. The code looks like it should work, but when I'm troubleshooting with Firebug, it appears that my for loop (that even Firebug shows having only 1 element in it) executes more than once and throws an error the second time around. Here's the Javascript:

function handleElements(elementid,containerid) {
// Get array of all 'visible' elements in the container
var elements = document.getElementById(containerid).getElementsByClassName('visible');

// Iterate over that array and make them all 'hidden'
for (var i in elements) {
    var object = elements[i].id;
    document.getElementById(object).className='hidden';
}

// Get the 'clicked' tab and set it to 'visible'
var clicked = document.getElementById(elementid);
clicked.className='visible';
}

And here is the HTML:

<div id="wrapper">
<div id="leftpanel">
    <div id="navcontainer">
        <ul id="navlist">
            <li><a href="javascript:;" onClick="handleElements('Modules','rightpanel')">Modules</a></li>
            <li><a href="javascript:;" onClick="handleElements('DataViewer','rightpanel')">Data Viewer</a><li>
            <li><a href="javascript:;" onClick="handleElements('Alarms','rightpanel')">Alarms</a><li>
            <li><a href="javascript:;" onClick="handleElements('Logging','rightpanel')">Logging</a><li>
            <li><a href="javascript:;" onClick="handleElements('Outputs','rightpanel')">Output Control</a><li>
        </ul>
    </div>
</div>

<div id="rightpanel">
    <div id="Modules" class="visible">
        <h2>Module Information Here</h2>
    </div>
    <div id="DataViewer" class="hidden">
        <h2>Data Viewer Here</h2>
    </div>
    <div id="Alarms" class="hidden">
        <h2>Alarm Page Here</h2>
    </div>
    <div id="Logging" class="hidden">
        <h2>Logging Setup Here</h2>
    </div>
    <div id="Outputs" class="hidden">
        <h2>Output Control Here</h2>
    </div>
</div>

iYeager
  • 47
  • 2
  • 7
  • What is the error message? – Adam Jun 22 '13 at 07:51
  • @everse: *`Uncaught TypeError: Cannot set property 'className' of null `* For some reason, even though `object` (odd name for it!) is a correct `id` value, `getElementById` is returning `null`. Replicated here: http://jsbin.com/evejid/1 (source: http://jsbin.com/evejid/1/edit). – T.J. Crowder Jun 22 '13 at 07:55

1 Answers1

0

There are two problems with your loop:

  1. You're looping through a list of elements, there's no need to then go try to get them by id. so change these two lines:

    var object = elements[i].id;
    document.getElementById(object).className='hidden';
    

    to:

    elements[i].className='hidden';
    

    Changing that fixes the problem: Compare this working version (source) to your original (source), but I haven't figured out yet why. :-) getElementById is returning null even though you've clearly assigned the ids (and in fact, we're getting them correctly from the elements).

  2. But while the above is something you need to fix, it's not why you're having the error. The error is coming from your using for-in to loop through the NodeList. Use a normal for loop:

    for (var i = 0; i < elements.length; ++i) {
    

    Remember that for-in is not about looping through array elements (and the return value from getElementsByClassName is not an array), it's about looping through the enumerable property names of an object. In your case, you see it loop twice: Once for the property "0", and once for the property "length". Since you don't have an element with the id "length", your next line blows up, since getElementById returns null for that id.

    More about the myths and realities of for-in on my blog, and more about various "correct" ways to loop through arrays (although again, that's not what elements is in your code) here on Stack Overflow.

Community
  • 1
  • 1
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Works like a charm. The first part makes sense, but the looping doesn't. I'm used to Python where the For-In method is best practice. I've never seen it react like this to an array. – iYeager Jun 22 '13 at 08:00
  • @iYeager: `for-in` in JavaScript is different from the similar construct in some other languages. I've figured what was going on, and it *is* the `for-in` that's the fundamental problem. I've updated the answer to explain what's going on and why, with links to things to help you avoid this problem in the future. – T.J. Crowder Jun 22 '13 at 08:12
  • Nice blog article, too bad I can't upvote you there too. Thanks for the assist. Cheers. – iYeager Jun 22 '13 at 08:17