Search code examples
javascriptevent-handlingaddeventlistenernodelistevent-delegation

How to iterate a node-list in order to assign index-specific event-handlers to each element-node?


I'm trying to use a for loop to increment through multiple button elements so I can assign different actions to them.

I cannot figure this out, Here is the code.

I am very much new to javascript and I've read through MDN docs and haven't come up with anything.

let numberOfbuttons = document.querySelectorAll(".numbers");

for (i = 0; i < numberOfbuttons; i++) {
  document.querySelectorAll(".numbers")[i].addEventListener("click", function() {
    document.querySelector(".result").innerHTML = "1"
  })
};
body {
  background-color: blanchedalmond;
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-template-rows: 1fr 1fr;
}

.Calc {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr 1fr;
  grid-template-rows: 1fr 1fr 1fr 1fr 1fr;
  grid-area: 2/2;
}

button[value="0"] {
  grid-area: 5/2/5/2;
}

button {
  background-color: aliceblue;
  color: black;
}

button[value="AC"] {
  grid-area: 5/1;
}

button[value="+"] {
  grid-area: 3/4;
}

button[value="-"] {
  grid-area: 2/4;
}

.result {
  grid-area: 1/1/1/5;
  background-color: #CBD2A4;
  text-align: center;
}
<!DOCTYPE html>
<html lang="en" dir="ltr">

<head>
  <meta charset="utf-8" />
  <title>Calculator</title>
  <link rel="stylesheet" href="styles.css" />
  <link href="https://fonts.googleapis.com/css?family=Arvo" rel="stylesheet" />
</head>

<body>
  <div class="Calc">
    <div class="result">
      <p>Type in your "numbers"</p>
    </div>

    <button class="numbers">1</button>
    <button class="numbers">2</button>
    <button class="numbers">3</button>
    <button class="numbers">4</button>
    <button class="numbers">5</button>
    <button class="numbers">6</button>
    <button class="numbers">7</button>
    <button class="numbers">8</button>
    <button class="numbers">9</button>
    <button class="numbers">0</button>
    <button value="AC">AC</button>
    <button value="+">+</button>
    <button value="-">-</button>
    <button value="*">x</button>
    <button value="/">/</button>
    <button value="=">=</button>
  </div>
  <script src="index.js" charset="utf-8"></script>
</body>

</html>


Solution

  • Is the OP aware of the fact that querySelectorAll actually returns a non-live NodeList which has its own forEach method for iterating each of its element nodes?

    But one anyhow would never register an event-listener for each element node, each with its customized handler-function, determined by the element's index.

    One rather would slightly improve the HTML-structure by grouping all the dials and make use of event-delegation.

    Thus one implements a single function which handles every dial-button click-event. This handler then gets registered once at the closest parent element of all dial-buttons.

    function handleCalculatorDialClick(evt) {
    
      const target = evt.target.closest('button');
      const { value } = target;
    
      const isSingleDigitDial = (/\d/).test(value);
    
      if (isSingleDigitDial) {
        target
    
          .closest('.calculator')
          .querySelector('output')
          .value = value;
    
      } /* else if () {
    
        // handle another dial
    
      }*/
    }
    document
      .querySelector('.calculator .dials')
      ?.addEventListener('click', handleCalculatorDialClick);
    body {
      margin: 0;
    }
    .calculator {
    
      display: grid;
      grid-template-rows: repeat(5, 1fr);
      grid-template-columns: repeat(4, 1fr);
      width: 45%;
    
      button {
        color: black;
        background-color: #fffaf0;
    
        &:focus {
          z-index: 1;
          outline: 2px solid lime;
        }
      }
    
      .display {
        grid-area: 1 / 1 / 1 / 4;
    
        background-color: #CBD2A4;
        padding: .95em;
        text-align:center;
      }
    
      .dials {
        grid-area: 2 / 1 / 5 / 4;
    
        display: grid;
        grid-template-rows: repeat(4, 1fr);
        grid-template-columns: repeat(4, 1fr);
    
        .non-zero-digits {
          grid-area: 1 / 1 / 4 / 4;
    
          display: grid;
          grid-template-rows: repeat(3, 1fr);
          grid-template-columns: repeat(3, 1fr);
    
          button {
            background-color: aliceblue;
          }
        }
        .linear {
          grid-area: 1 / 4 / 3 / 4;
    
          display: grid;
          grid-template-rows: repeat(2, 1fr);
          grid-template-columns: repeat(1, 1fr);
        }
        .geometric {
          grid-area: 4 / 2 / 4 / 4;
    
          display: grid;
          grid-template-rows: repeat(1, 1fr);
          grid-template-columns: repeat(2, 1fr);
        }
        button[value="0"] {
          grid-area: 3 / 4;
    
          background-color: aliceblue;
        }
        button[value="="] {
          grid-area: 4 / 4;
    
          background-color: #ffebc5;
        }
        button[value="AC"] {
          grid-area: 4 / 1;
    
          background-color: #ffd88c;
        }
      } 
    }
    <link href="https://fonts.googleapis.com/css?family=Arvo" rel="stylesheet" />
    
    <div class="calculator">
      <output class="display">
       Type in your "numbers"
      </output>
    
      <div class="dials">
        <div class="non-zero-digits">
          <button value="1">1</button>
          <button value="2">2</button>
          <button value="3">3</button>
          <button value="4">4</button>
          <button value="5">5</button>
          <button value="6">6</button>
          <button value="7">7</button>
          <button value="8">8</button>
          <button value="9">9</button>
        </div>
        <button value="0">0</button>
    
        <div class="linear">
          <button value="+">+</button>
          <button value="-">-</button>
        </div>
        <div class="geometric">
          <button value="*">x</button>
          <button value="/">/</button>
        </div>
    
        <button value="=">=</button>
        <button value="AC">AC</button>  
      </div>
    </div>