Jawad
Jawad

Reputation: 3

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>

Upvotes: 0

Views: 110

Answers (3)

cyber_steppa
cyber_steppa

Reputation: 1

I see a couple of issues with the JavaScript part of the code. Let's walk through them and correct them:

  1. document.querySelectorAll(".numbers") returns a NodeList, but in your for loop, you're using numberOfbuttons directly, which will result in an error because you can't iterate over a NodeList like an integer. Instead, you need to get the length of the NodeList.
  2. The grid-area for the button[value="0"] is incorrect (grid-area: 5/2/5/2;). The grid-area property should be written as four values representing the start-row, start-column, end-row, and end-column, but the values you used are duplicated. If this was intended to position the button, it might need adjusting.
  3. You are statically setting the result to "1" for all buttons. You might want to dynamically set the result to the button's value when clicked.
let numberOfButtons = document.querySelectorAll(".numbers");

for (let i = 0; i < numberOfButtons.length; i++) {
  numberOfButtons[i].addEventListener("click", function() {
    document.querySelector(".result").innerHTML = this.innerHTML; // Update result with clicked button's value
  });
}

Upvotes: 0

user27425627
user27425627

Reputation: 1293

You're almost there. querySelector returns a list, not its length. You can try this:

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

for(i = 0; i < buttons.length; i++){
  buttons[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>

N.B. This is the most simple, immediate answer to the given issue, adapted to the context:

I am very much new to javascript.

For the better, more general solution, but which changes the OP's approach entirely, see @Peter Seliger's answer.

Upvotes: 0

Peter Seliger
Peter Seliger

Reputation: 13432

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>

Upvotes: 0

Related Questions