简体   繁体   中英

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. (ie "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>   

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");
    }
}

Here's another way to a solution utilizing some different APIs. The explanations and references are commented in the source.

FIDDLE

SNIPPET

 /* Footnotes at the end */ // Alt. var filter = document.querySelector('#filterName');¹ var filter = document.getElementById('filterName'); /* Use the Event Object to in order to determine event.target² || (the element that was clicked) || Event Object----------------------------⇲ */ filter.addEventListener("change", function(event) { // Save event.target's value in the variable 'employee' var employee = event.target.value; // Call filterName and pass in 'employee' filterName(employee); }, false); function filterName(name) { // Alt. nodeNames = document.querySelectorAll('.' + name);³ var nameNodes = document.getElementsByClassName(name); // Convert nodeNames to an array⁴ var nameArray = Array.prototype.slice.call(nameNodes); console.log(nameArray.length); /* Iterate through nameArray and on each iteration || use `classList.add`⁵ to add the .hidden class. */ for (var i = 0; i < nameArray.length; i++) { nameArray[i].classList.add("hidden"); } } /* ¹ https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector || ² https://developer.mozilla.org/en-US/docs/Web/API/EventTarget || ³ https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll || ⁴ https://developer.mozilla.org/en-US/docs/Web/API/NodeList#Converting_a_NodeList_to_an_Array || ⁵ https://developer.mozilla.org/en-US/docs/Web/API/Element/classList */ 
 .hidden { display: none; } 
 <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet"> <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> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script> 

REFERENCES

  1. querySelector
  2. EventTarget
  3. querySelectorAll
  4. Converting a NodeList to an Array
  5. classList

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM