perkface perkface - 7 months ago 11
HTML Question

getElementsByClassName Showing Correct Length, but Not Executing Code on Third (Last) Result

JSFiddle for this issue.

I've got a function that executes when you change the value of the dropdown. It selects all elements with the class name that corresponds with the value of the select option. (i.e. "Ralph" selects all div elements with the classname "Ralph").

From there, I've attemtped to iterate through the class array things (I'm not too great with programming vocabulary). I thought it was working great. Then I realized, that it will not hide the third "Dan" element.

It does hide it after I select another value from the select drop down, and then select "Dan" again.

There is no error in the console that I can see. I've tried the following:


  1. Adding +1 to the x.length of the IF statement to try and force it to do it again.

  2. Setting i = -1 (stupid, I know. Spoiler, it didn't work).



In the console, you'll note that I log the initial length of the var x (which holds the class names). It is correctly identifying that there are 3 "Dan" elements. I'm stumped as to why it's not hiding the last one.

JS:

var x;
function filterName(n) {
x = document.getElementsByClassName(n);
console.log(x.length);
for (i = 0; i < x.length; i++) {
x[i].setAttribute("class", "hidden");
}
}


HTML:

<div class="container">
<div class="names col-sm-12">
<form name="filterDB" action="POST">
<select id="filterName" name="filterName">
<option value="" disabled="" selected="">Filter by Employee: </option>
<option id="Ralph" name="name" value="Ralph">Ralph</option>
<option id="Dan" name="name" value="Dan">Dan</option>
<option id="Brady" name="name" value="Brady">Brady</option>
<option id="Abby" name="name" value="Abby">Abby</option>
</select>
</form>
</div>

<div class="col-md-2 col-md-offset-1">
<h4 class="titles">Monday</h4>
<hr>
<div class="Ralph">Name: Ralph<hr></div>
</div>
<div class="col-md-2">
<h4 class="titles">Tuesday</h4>
<hr>
<div class="Dan">Name: Dan<hr></div>
<div class="Dan">Name: Dan<hr></div>
</div>
<div class="col-md-2">
<h4 class="titles">Wednesday</h4>
<hr>
<div class="Brady">Name: Brady<hr></div>
</div>
<div class="col-md-2">
<h4 class="titles">Thursday</h4>
<hr>
<div class="Abby">Name: Abby<hr></div>
</div>
<div class="col-md-2">
<h4 class="titles">Friday</h4>
<hr>
<div class="Dan">Name: Dan<hr></div>
</div>
<div class="clearfix"></div>
</div>

Answer

As I said in my comment... getElementsByClassName returns a Live HTMLCollection meaning that it will change as you change the DOM

Your best option would be to copy the objects into an array before you process them. Something like this...

function filterName(n) {
    var x = document.getElementsByClassName(n);
    var objs = [];
    for (i = 0; i < x.length; i++) {
        objs.push(x[i]);
    }
    for (i = 0; i < objs.length; i++) {
        objs[i].setAttribute("class", "hidden");
    }
}

Another option could be to go through the elements in reverse... or create a while loop that looks at the first item in the array until there are no more items. Something like this...

function filterName(n) {
    var x = document.getElementsByClassName(n);
    while (x.length > 0) {
        x[0].setAttribute("class", "hidden");
    }
}
Comments