5

I am attempting to get a better understanding of Javascript classes. By doing so I am dynamically creating player scorecards. There is an input and submit button to create as many cards as you would like. Each created card also has its own input and submit box. When I add a value to a specific cards input box and click submit, the inner html that is created goes to the final card created instead of the current. Here is a snippet of my codebase. Also from doing some research (I could be mistaken of course), closures and classes (syntactic sugar) are different in there approach https://medium.com/engineering-livestream/javascript-classes-vs-closures-cf6d6c1473f and so not sure how closures would in affect apply as most examples use regular functions.

/*************************************************************************************************/

//Creates the Players score card

/*************************************************************************************************/
export default class CreateScoreCard {
    constructor()
    {
      this.plyCdCmplte = null;
      this.plyCardWrapper = null;
    }
  /*************************************************************************************************/
  createPlayerCardUpper()
  {
    let plyCdGrd,
      plyCdTtle,
      plyNm,
      plyScore,
      plyScTxtNd,
      plyNmTxtNd;

    this.plyCdCmplte = document.getElementById('player-card-complete');
    this.plyCardWrapper = document.createElement('div');
    this.plyCardWrapper.setAttribute('class', 'player-card-wrapper');

    plyCdGrd = document.createElement('div');
    plyCdTtle = document.createElement('div');
    plyNm = document.createElement('div');
    plyScore = document.createElement('div');

    plyCdGrd.setAttribute('class', 'player-card-grid');
    plyCdTtle.setAttribute('class', 'player-card-title');
    plyNm.setAttribute('class', 'player-name');
    plyNmTxtNd = document.createTextNode('Player One');
    plyNm.appendChild(plyNmTxtNd);
    plyScore.setAttribute('class', 'player-score');
    plyScTxtNd = document.createTextNode('0');
    plyScore.appendChild(plyScTxtNd);

    this.plyCdCmplte.appendChild(this.plyCardWrapper);
    this.plyCardWrapper.appendChild(plyCdGrd);
    plyCdGrd.appendChild(plyCdTtle);
    plyCdTtle.appendChild(plyNm);
    plyCdTtle.appendChild(plyScore);
  }
  /*************************************************************************************************/
  createPlayerCardMid()
  {
      let sttngsGrd,
      sttngsCntr,
      sttngsBrs,
      frmDvPnts,
      pntsBtnTxtNd;

      sttngsGrd = document.createElement('div');
      sttngsGrd.setAttribute('class', 'settings-grid');
      sttngsCntr = document.createElement('div');
      sttngsCntr.setAttribute('class', 'settings-container');
      sttngsBrs = document.createElement('i');
      sttngsBrs.setAttribute('class', 'fas fa-bars settings-bars');
      frmDvPnts = document.createElement('div');
      frmDvPnts.setAttribute('class', 'form-div-points');
      this.inptPts = document.createElement('input');
      this.inptPts.type = 'number';
      this.inptPts.min = '1';
      this.inptPts.value = '1';
      this.inptPts.setAttribute ('class', 'input-points')
      this.pntsBtn = document.createElement('button');
      this.pntsBtn.setAttribute('class', 'points-btn');
      pntsBtnTxtNd = document.createTextNode('ADD');
      this.pntsBtn.appendChild(pntsBtnTxtNd);

      this.plyCardWrapper.appendChild(sttngsGrd);
      sttngsGrd.appendChild(sttngsCntr);
      sttngsCntr.appendChild(sttngsBrs);
      sttngsGrd.appendChild(frmDvPnts);
      frmDvPnts.appendChild(this.inptPts);
      frmDvPnts.appendChild(this.pntsBtn);    
  }
  /*************************************************************************************************/
  createPlayerCardLower()
  {
      this.plyCdBdy = document.createElement('div');
      this.plyCdBdy.setAttribute('class', 'player-card-body');
      this.plyCardWrapper.appendChild(this.plyCdBdy);
  }
  /*************************************************************************************************/
  getPlayerScoreFromUserInput()
  {
      let pointsBtn = document.getElementsByClassName('points-btn');
      let inputPoints = document.getElementsByClassName('input-points');

      let playerCardBody = document.getElementsByClassName('player-card-body');

        for(let i = 0; i < playerCardBody.length; i++)
      {
          let numFromInptArr = [];
          let sumOfUserInputNums;

          pointsBtn[i].addEventListener('click', ()=> 
          {
              inputPoints[i].value;

              numFromInptArr.push(+inputPoints[i].value)

              sumOfUserInputNums = numFromInptArr.reduce((a, b)=>a+b);

              this.playerScore = document.getElementsByClassName('player-score');

              this.playerScore[i].innerHTML = sumOfUserInputNums;

              this.createRoundScoreHtml(1, inputPoints[i].value)
          })
      }  

      // This is the forEach method of looping through eventListener's
     /*  Object.keys(pointsBtn).forEach(function (key){
          pointsBtn[key].addEventListener('click', ()=> {
              console.log(inputPoints[key].value);
          })
      }); */
  }
  /*************************************************************************************************/
  createRoundScoreHtml(roundNum, roundScore)
  {
      // Using this method to create the html content
      this.plyCdBdy.insertAdjacentHTML("beforeend", 
          `<div class = "round-container">
          <span class = "round-text">
          Round
          <span class = "round-number">
          ${roundNum}
          <span class="score-text">Score</span>
          </span>
          </span>
          <span class = "round-score">${roundScore}</span></div>`);
  }
  /*************************************************************************************************/
  getNumOfPlayers()
  {
       this.numOfPlayersInput = document.getElementById('num-players-input');
       this.numPlayersSubmitBtn = document.getElementById('num-players-submit-btn');
       this.numPlayersCntnr = document.getElementById('num-players-cntnr');

      // When user updates the input form with a num and then clicks
      // on the submit btn, will iterate the amount of players
      this.numPlayersSubmitBtn.addEventListener('click', (event) =>
      {
          event.preventDefault();

          // Clears the card from the html 
          if (this.plyCdCmplte !== null)
          {
              this.plyCdCmplte.innerHTML = '';
          }

          // Number of players submitted from the number of players input box
          for(let iterPlayers = 0; iterPlayers < this.numOfPlayersInput.value; iterPlayers++)
          {
              this.createPlayerCardUpper();
              this.createPlayerCardMid();
              this.createPlayerCardLower();
          }
          this.getPlayerScoreFromUserInput();

      })
  }
  /*************************************************************************************************/
  }
ShowstopperCode1
  • 228
  • 1
  • 4
  • 15
  • Possible duplicate of [JavaScript closure inside loops – simple practical example](https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – skyline3000 Jul 26 '18 at 03:00
  • 1
    Could you add clarity on why you believe it is a duplicate? – ShowstopperCode1 Jul 26 '18 at 03:19
  • 2
    You really should provide a [mcve]. The JavaScript doesn't look like it is minimal, and the HTML is far from complete. – Quentin Jul 27 '18 at 14:44

1 Answers1

0

The reason for the newly added score to go to the last created card is the following statement

this.playerScore = document.getElementsByClassName('player-score')
this.playerScore[i].innerHTML = sumOfUserInputNums;

Problems with this:

  1. document.getElementsByClassName returns an array that contains all the elements with that matching className. Seeing your example you will have lots of those elements. Consider using id's or add some identifier token on the className itself such as score3-player1.
  2. this.playerScore[i] i will not remain consistent with your expectation as the array items keep changing with time when you add new cards. The elements in the returned collection by getElementsByClassName are sorted as they appear in the source code. [Src <- W3Schools]

Take a look at an answer of mine that talks about locating certain HTML elements' associated Array element index to help you better target your cards: https://stackoverflow.com/a/51560133/5642455

  • 1
    awww I think I get it now!! From looking at the example link and what you wrote I can dynamically increment an id or class identifier. Then if that id/class is true, add the html content to that card – ShowstopperCode1 Jul 27 '18 at 15:06