Home > database >  Javascript eventlistener in loop function runs too many times
Javascript eventlistener in loop function runs too many times

Time:05-19

I have a couple of buttons which I'm trying to loop through and add a event listener function to them. I do this by using the following code:

function handleButtonClick({
    target
    }) {
    const btnNode = target.closest('button');
    console.log('hi')
}
const btns = document.querySelectorAll('.answerBtn');

btns.forEach(btns => {
    btns.addEventListener('click', handleButtonClick)
})

Everytime a button is clicked it should run the function and it does. The only problem is that if I have 4 buttons, it runs the first button 4 time, the 2th button 3 times, the 3th button 2 times and the last ones. I know this happens because of the loop, but is there a way to make sure that it doesn't matter on what button I press, it only runs the function once?

let clickCountStorage = new Map;

class Question {
  /**
   * @param {string} question
   * @param {string} prefix
   * @param {string} description
   * @param {string} display
   * @param {string} answerType
   * @param {string} multiSelect
   * @param {Answer[]} answers
   */
  constructor(question = "", prefix = "", description = "", display = "", answerType = "", multiSelect = "",
    answers = []) {
    this.question = question;
    this.prefix = prefix;
    this.description = description;
    this.display = display;
    this.answerType = answerType;
    this.multiSelect = multiSelect;
    this.answers = answers;
  }
}


class Answer {
  constructor(id = "", name = "") {
    this.id = id;
    this.name = name;
  }
}

function createButton(id) {

  let generateNewAnswer = document.createElement('button');
  generateNewAnswer.setAttribute('type', 'button');
  generateNewAnswer.id = `answerBtn${ id }`;
  generateNewAnswer.classList.add('answerBtn');
  generateNewAnswer.innerHTML = 'Add Answer';

  return generateNewAnswer

}

function main() {
  function handleButtonClick() {
    let target = event.target;

    const btnNode = target.closest('button');

    const buttonClickTotal = clickCountStorage.get(btnNode)   1;
    clickCountStorage.set(btnNode, buttonClickTotal);

    const clickCountList = Array
      .from(
        clickCountStorage.values()
      );

    const allButtonsClickTotal = clickCountList
      .reduce((total, count) => total   count, 0);

    const AllBtnsClickedThreeTimes = clickCountList
      .every(count => count >= 3);

    console.log({
      buttonClickTotal,
      allButtonsClickTotal,
      AllBtnsClickedThreeTimes,
    });
  }

  const btns = document.querySelectorAll('.answerBtn');
  console.log(btns)

  btns.forEach((btn) => {
    btn.addEventListener('click', handleButtonClick);
    console.log(btn)
  })
}


class Structure {
  constructor() {
    /**
     * @type {Question[]}
     */
    this.questions = [];
    this.clickCount = 0;
    this.currentQuestion = this.questions.length;
    this.displayArr = ["Selecteer soort:", "button", "colorBtn", "position", "dropdown"];
    this.typeArr = ["Selecteer type:", "max", "all"];
    this.MultiArr = ["Multiselect:", "true", "false"];
  }

  AddQuestion() {
    let currentQuestion = this.questions.length;

    // The new created question which has to be added to the questions.
    let newQuestion = new Question();
    // Push the new question to the question list.
    this.questions.push(newQuestion);

    // The div generator for the answers
    let answerDIV = document.createElement('div');
    answerDIV.className = 'answerDIV'   currentQuestion;
    answerDIV.id = 'AnswerDivId'   currentQuestion;
    document.getElementsByClassName('create')[0].appendChild(answerDIV);

    let generateNewAnswer = createButton(currentQuestion);

    clickCountStorage.set(generateNewAnswer, 0)

    generateNewAnswer.onclick = _ => this.AddAnswer(currentQuestion);

    document.getElementsByClassName('create')[0].appendChild(generateNewAnswer);

  }

  /**
   * @param {int} workingQuestionIndex
   */
  AddAnswer(workingQuestionIndex) {
    let workingQuestion = this.questions[workingQuestionIndex];

    let newAnswerIndex = workingQuestion.answers.length;
    let newAnswerId = 'id'   newAnswerIndex;

    // The new answer to insert.
    let newAnswer = new Answer(newAnswerId);
    // Add the new answer to the total answers.
    workingQuestion.answers.push(newAnswer);

    let idElement = document.createElement('input');
    idElement.setAttribute('type', 'text');
    idElement.name = "id";
    idElement.id = newAnswerId;
    idElement.classList.add('id', 'QuestionNumber'   workingQuestionIndex);
    idElement.placeholder = 'Add the ID of the answer';
    idElement.addEventListener('input', function(_) {
      newAnswer.id = this.value;
    });

    // Appends the answers to the AnswerDiv
    document.getElementsByClassName('answerDIV'   workingQuestionIndex)[0].appendChild(idElement);
  }
}

class GenerateArray {

  constructor() {
    this.structure = new Structure();
  }

  generateQuestionPart() {
    this.structure.AddQuestion();
  }

}

let newQuestion = new Question();
let struc = new Structure();
NewArray = new GenerateArray();

document.querySelectorAll('.QPB')[0].addEventListener('click', () => {
  main()
})
body {
  margin: 0;
  padding: 0;
}

.container {
  height: 1000px;
  width: 800px;
  position: relative;
  margin-top: 5px;
  left: 50%;
  -ms-transform: translate(-50%, 5%);
  transform: translate(-50%, 5%);
}

.QPB {
  position: relative;
  float: left;
  margin-left: 15px;
  margin-top: 10px;
  margin-bottom: 10px;
  border: none;
  border-radius: 0.25rem;
  color: white;
  font-size: 40px;
  background-color: #ff5c01!important;
  width: 50px;
  height: 50px;
}

.question,
.prefix,
.description {
  position: relative;
  margin-right: 5px;
  width: 95%;
  height: 30px;
  margin-bottom: 10px;
  margin-left: 15px;
}

.SelClassD,
.SelClassT,
.SelClassM {
  position: relative;
  margin-right: 5px;
  width: 20%;
  height: 30px;
  margin-bottom: 10px;
  margin-left: 15px;
  border: 2px solid #ced4da;
  border-radius: 0.5rem;
}

.SelClassD:focus,
.SelClassT:focus,
.SelClassM:focus {
  outline: none !important;
  border: 4px solid rgb(135, 206, 250, 0.5);
  border-radius: 0.5rem;
}

.question,
.description,
.prefix,
.id,
.name {
  border: 2px solid #ced4da;
  border-radius: 0.5rem;
}

.question:focus,
.description:focus,
.prefix:focus,
.id:focus,
.name:focus {
  outline: none !important;
  border: 4px solid rgb(135, 206, 250, 0.5);
  border-radius: 0.5rem;
}

.id,
.name {
  position: relative;
  width: 90%;
  height: 30px;
  margin-bottom: 10px;
  margin-left: 55px;
}

.answerBtn {
  width: 100px;
  height: 40px;
  background-color: #ff5c01!important;
  color: white;
  border: none;
  border-radius: 0.25rem;
  margin-bottom: 50px;
  margin-left: 15px;
}

.CreateArray {
  width: 95%;
  margin-left: 20px;
  margin-bottom: 10px;
  height: 40px;
  background-color: #3db556!important;
  color: white;
  border: none;
  border-radius: 0.25rem;
}


/* card */

.DONOT {
  margin-left: 15px;
  font-style: italic;
  font-size: 24px;
}

.card-body-home {
  border: 2px solid rgba(0, 0, 0, .125);
  border-bottom: none;
  border-radius: 3px 3px 0 0;
}


/* form card */

.form-card-DT {
  max-width: 800px;
  border: none!important;
  height: 100%;
  /* padding-bottom: 10px; */
}

.form-card-header {
  border: none;
  background-color: #ff5c01!important;
  color: white;
  font-weight: 500;
  font-style: italic;
  font-size: 24px;
  padding-top: 10px;
  padding-left: 15px;
  border-radius: 0!important;
  height: 35px;
}

.form-card-body {
  border-radius: 0;
  border: solid 1px #b5b5b5;
  border-top: none;
}
<div style='width: 1000px;margin: auto;'>
  <div class='card text-dark bg-light mb-3 form-card-DT'>
    <div class='card-header form-card-header'>Creeër een vragenlijst:</div>
    <div class='card-body form-card-body'>
      <div >Gebruik het volgende teken niet ivm error: '</div>
      <div >
        <button id="QuestionPartBtn"  type="button" onclick="NewArray.generateQuestionPart()"> 
                    </button>
        <br><br>
      </div>
      <div >
        <button id="download-btn" >Generate Code</button>
      </div>
    </div>
  </div>
</div>

CodePudding user response:

const btns = document.querySelectorAll(".answerBtn");

btns.forEach(btn => {
    btn.addEventListener("click", () => handleButtonClick(btn))
});

function handleButtonClick(btn){
    if(btn.style.background !== 'red'){
        btn.style.background = 'red'
    }else{
        btn.style.background = 'grey'
    }
  console.log(btn)
}
<button >0</button>
<button >1</button>
<button >2</button>
<button >3</button>

This should do the trick. Tested it on code pen.

CodePudding user response:

You can also try to use Event Bubbling and assign only one "click" event listener to the closest common parent

  • Related