Stack in Overflow
Stack in Overflow

Reputation: 5

Adding to array at each button click javascript

I'm trying to create an array and storing data in the array every time a button is pushed.

const allButtonsToStore = document.getElementsByClassName("button");
let reason = [];
for (x=0; x < allButtonsToStore.length; x++) {
  allButtonsToStore[x].addEventListener("click", function(){
    /*reason.push([x]); not working*/
    reason[x] = this.textContent;
    console.log(reason[x]);
  });
}

So what I want the output to be: reason0 = some textContent, reason1 = some textContent, reason2 = some textContent, etc.

But this is not working. When, in chrome, I console log reason[0], it outputs "undefined". When I log reason[20] it outputs the current content of the button clicked. reason[20] will get overwritten when another button is pushed. console.log(reason[x]); does correctly output the current button clicked.

It looks like the array is not working. When I add console.log([x]); to the script in the for loop, it does output all the numbers individually. I must be making a newbie mistake.

Upvotes: 0

Views: 1918

Answers (3)

zer00ne
zer00ne

Reputation: 43880

brk's answer is correct. This answer offers a better solution to OP objective. Instead of adding an event listener to each button, add an event listener to an ancestor tag that all of the buttons have in common. An ancestor tag is an element that contains the targeted element(s). The ancestor can also be an Object such as window or document:

Example

<!------------------[Listener] event.currentTarget or this
                        --==--
The tag/object registered to the event.
It will listen for event to be triggered for itself as well as 
all of its descendant tags.
                         --==--
ex. const tag = document.querySelector(selector)
    tag.addEventListener(eventType, eventHandler)
                         --==--
    selector: "fieldset", "#fs0", ".set", "[name=group]", etc
    eventType: "click", "change", "input", etc
    eventHandler: a function that runs when event is triggered
==------------------------------------------------------------->

<!--[Ancestor Object]-- window ------------------------------>
<!----[Ancestor Object] document -------------------------->
<!-------[Ancestor Tag] document.documentElement -------->
<html>
<head></head>
<!------[Ancestor Tag]-- document.body ---------------------->
<body>
  <!------[Ancestor Tag] document.forms[0] ---------------->
  <form>
    <!------[Ancestor/Parent Tag] document.forms[0].elements[0]
                             --==--
    It is recommended that the closest ancestor tag be 
    registered to the event. 
    There are some exceptions.
                             --==--
    ex. window/document for "keydown", or "keypress"
        <form> for "change", "input", "reset", or "submit" 
    ==--------------------------------------------------->
    <fieldset id='fs0' class='set' name='group'>

      <!----[Targeted Tags] event.target
                     --==--
      The origin of registered event.
                     --==--
      ex. button clicked by user or 
          input that user inputed text into 
      ==----------------------------------------------->
      <button type='button'>Target</button>
      <button type='button'>Target</button>
      <button type='button'>Target</button>
    </fieldset>
  </form>
</body>
</html>

The ancestor that is registered to an event (loosely termed as a "listener") can listen for its descendant tags due to event bubbling. To effectively call the event handler function when only a button is clicked or when a specific key is stroked, the event handler will control such behavior by passing the Event Object and using its properties to determine what was clicked (ie event.target) or what is the listener (ie event.currentTarget), etc. This programming pattern is called Event Delegation which is by far a better choice than adding event listeners to each and every tag by a loop.

Advantages

  • Only need to register a single tag to the event to cover an unlimited number of tags.

  • Any tags added dynamically to the listener after the webpage has loaded are covered as well.


Demo

const fs = document.querySelector("fieldset");
fs.addEventListener('click', getText);

let AE = [];

function getText(event) {
  const clicked = event.target;
  if (clicked.tagName === "BUTTON") {
    AE.push(clicked.textContent);
    console.log(JSON.stringify(AE));
  }
}
.as-console-wrapper {
  word-break: break-word;
}
<fieldset>
  <button>Alpha</button>
  <button>Beta</button>
  <button>Gamma</button>
  <button>Delta</button>
  <button>Epsilon</button>
</fieldset>

Upvotes: 0

Mamun
Mamun

Reputation: 68933

As variable (i in the loop) declared without keyword and with var they are declared as global variable. By the time you click that variable only has the last value of the iteration

To solve the issue you can either use IIFE (Immediately Invoked Function Expression) or declare the valiable with let as this will create a block scope local variable.

for (let x=0; x < allButtonsToStore.length; x++) {.....

Using IIFE (Immediately Invoked Function Expression)

for (x=0; x < allButtonsToStore.length; x++) {
  (function () {
    allButtonsToStore[x].addEventListener("click", function(){
      reason[x] = this.textContent;
      console.log(reason[x]);
    });
  })();
}

Upvotes: 1

brk
brk

Reputation: 50291

Here for (x=0; x < allButtonsToStore.length; x++) { x becomes a global variable. Use let to scope x

for (let x=0; x < allButtonsToStore.length; x++) { // rest of code

Upvotes: 4

Related Questions