Home > OS >  Last iteration(?) of a NodeList causing Uncaught TypeError
Last iteration(?) of a NodeList causing Uncaught TypeError

Time:11-16

The code works just as expected but if I use for (let line in hamburgerLines) {...} I get the following error: "Uncaught TypeError: Cannot read properties of undefined (reading 'toggle') at HTMLDivElement.addClass".

I used this alert function alert("Here: " line " " hamburgerLines[line] " " hamburgerLines.length); to find out what's happening. It iterates normally three times and hamburgerLines.length does return 3 but then pops up another time putting out: "Here: entries function entries() { [native code] } 3" which I assume causes the error.

I already fixed this by changing it to for (let line = 0; line < hamburgerLines.length; line ) {...} but would like to know why it iterates once more through the for loop.

let hamburgerButton = document.getElementById('hamburger-button');
hamburgerButton.addEventListener('click', addClass);

function addClass() {
  let hamburgerLines = document.querySelectorAll('.hamburger-line');
  for (let line in hamburgerLines) {
    hamburgerLines[line].classList.toggle('open-test');
  }
}
header #hamburger-button {
  position: absolute;
  width: 50px;
  height: 40px;
  top: 30px;
  right: 50px;
  cursor: pointer;
}


/* ---- Untoggled Hamburger Button ---- */

header .hamburger-line:nth-child(1) {
  width: 1px;
  height: 30px;
  background-color: #000;
  position: absolute;
  right: 0px;
  -webkit-transition: .5s ease-in-out;
}

header .hamburger-line:nth-child(2) {
  width: 1px;
  height: 20px;
  background-color: #000;
  position: absolute;
  right: 10px;
  -webkit-transition: .5s ease-in-out;
}

header .hamburger-line:nth-child(3) {
  width: 1px;
  height: 14px;
  background-color: #000;
  position: absolute;
  right: 20px;
  -webkit-transition: .5s ease-in-out;
}


/* ---- Toggled Hamburger Button ---- */

header .hamburger-line.open-test:nth-child(1) {
  width: 1px;
  height: 30px;
  background-color: #F00;
  position: absolute;
  right: 10px;
  -webkit-transition: .5s ease-in-out;
  transform: rotate(45deg);
}

header .hamburger-line.open-test:nth-child(2) {
  width: 1px;
  height: 20px;
  background-color: #FFF;
  position: absolute;
  transform: translateY(20px);
  -webkit-transition: .5s ease-in-out;
  opacity: 0;
}

header .hamburger-line.open-test:nth-child(3) {
  width: 1px;
  height: 30px;
  background-color: #F00;
  position: absolute;
  right: 10px;
  -webkit-transition: .5s ease-in-out;
  transform: rotate(-45deg);
}
<header>
  <div id="hamburger-button">
    <span class="hamburger-line"></span>
    <span class="hamburger-line"></span>
    <span class="hamburger-line"></span>
  </div>
</header>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

CodePudding user response:

document.querySelectorAll() returns a NodeList object, check NodeList documentation

Don't use for...in to enumerate the items in NodeLists, since they will also enumerate its length and item properties and cause errors if your script assumes it only has to deal with element objects. Also, for..in is not guaranteed to visit the properties in any particular order.

for...of loops will loop over NodeList objects correctly:

Recent browsers also support iterator methods (forEach()) as well as entries(), values(), and keys().

CodePudding user response:

You should use for..of loop instead of for..in and toggle the class as:

line.classList.toggle('open-test');

addClass function

function addClass() {
    let hamburgerLines = document.querySelectorAll('.hamburger-line');
    for (let line of hamburgerLines) {
        line.classList.toggle('open-test');
    }
}
  • Related