user3760657
user3760657

Reputation: 397

Changing innerHTML only works once

I'm attempting to make a scoreboard in HTML, where a javascript function can change the scores for various players. However, the scores for all but one of the players don't display correctly.

HTML:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
</head>
<body>
    <div id="scoreboard"></div>
    <script type="text/javascript" src="main.js"></script>
</body>
</html>

Javascript:

function Player(ID) {
    this.ID = ID;

    var scoreboard = document.getElementById("scoreboard");

    //Create a <p> in scoreboard with "Player n:" in it, and then
    //create a <span> with the score and a unique ID to use for getting and setting
    scoreboard.innerHTML += "<p>Player " + this.ID + ":" + "<span id=\"PlayerScore" + this.ID + "\">0</span></p>";

    this.scoreElement = document.getElementById("PlayerScore" + this.ID); //set score element

    //Getter and setter for score
    this.getScore = function() {return Number(this.scoreElement.innerHTML);}
    this.setScore = function(scoreIn) {this.scoreElement.innerHTML = scoreIn.toString();}
}

I test this code using the following Javascript:

var player1 = new Player(1);
var player2 = new Player(2);
var player3 = new Player(3);

player1.setScore(player1.getScore() + 1);
player2.setScore(player2.getScore() + 1);
player3.setScore(player3.getScore() + 1);

After doing this, the scoreboard in the generated webpage reads:

Player 1:0
Player 2:0
Player 3:1

However, it should read

Player 1:1
Player 2:1
Player 3:1    

Any ideas? Why does only the last one work? Is it a problem with how I'm getting and setting directly from and to the InnerHTML of the scoreElement? Does it have anything to do with my browser (firefox)?

Upvotes: 2

Views: 3552

Answers (2)

Arun P Johny
Arun P Johny

Reputation: 388446

The problem is your use of innerHTML to append more content, once you use innerHTML += it will recreate all the dom elements so your scoreElement references you created previously will no longer present in the dom tree that is why those are not getting updated.

The way to solve it is not to use .innerHTML to append content, instead you can create dom elements and call the appendChild() method to add it to the parent element like

function Player(ID) {
  this.ID = ID;

  var scoreboard = document.getElementById("scoreboard");

  var playerElement = document.createElement('p');
  this.scoreElement = document.createElement('span');
  this.scoreElement.innerHTML = 0;
  playerElement.appendChild(document.createTextNode("Player " + this.ID + ":"));
  playerElement.appendChild(this.scoreElement);
  scoreboard.appendChild(playerElement)

  //Getter and setter for score
  this.getScore = function() {
    return Number(this.scoreElement.innerHTML);
  }
  this.setScore = function(scoreIn) {
    this.scoreElement.innerHTML = scoreIn.toString();
  }
}

var player1 = new Player(1);
var player2 = new Player(2);
var player3 = new Player(3);

player1.setScore(player1.getScore() + 1);
player2.setScore(player2.getScore() + 1);
player3.setScore(player3.getScore() + 1);
<div id="scoreboard"></div>


As @dandavis said in the comments, another tweak you can do is

function Player(ID) {
  this.ID = ID;

  var scoreboard = document.getElementById("scoreboard");

  var playerElement = document.createElement('p');
  playerElement.innerHTML = "Player " + this.ID + ":" + '<span>0</span>';
  this.scoreElement = playerElement.querySelector('span');
  scoreboard.appendChild(playerElement)

  //Getter and setter for score
  this.getScore = function() {
    return Number(this.scoreElement.innerHTML);
  }
  this.setScore = function(scoreIn) {
    this.scoreElement.innerHTML = scoreIn.toString();
  }
}

var player1 = new Player(1);
var player2 = new Player(2);
var player3 = new Player(3);

player1.setScore(player1.getScore() + 1);
player2.setScore(player2.getScore() + 1);
player3.setScore(player3.getScore() + 1);
<div id="scoreboard"></div>

Upvotes: 10

MoLow
MoLow

Reputation: 3084

the problem, every time you call the constructor, it rebuilds scoreboard.innerHTML, meaning all its child elements are deleted and rebuilt,

so only in your last instance, this.scoreElement dose not get rebuilt.

you could solve this either using appendChild or by getElementById every time you get or set the score

function Player(ID) {
    this.ID = ID;

    var scoreboard = document.getElementById("scoreboard");

    //Create a <p> in scoreboard with "Player n:" in it, and then
    //create a <span> with the score and a unique ID to use for getting and setting
    scoreboard.innerHTML += "<p>Player " + this.ID + ":" + "<span id=\"PlayerScore" + this.ID + "\">0</span></p>";


    //Getter and setter for score
    this.getScore = function() {return Number(document.getElementById("PlayerScore" + this.ID).innerHTML);}
    this.setScore = function(scoreIn) { document.getElementById("PlayerScore" + this.ID).innerHTML = scoreIn.toString();}
}


var player1 = new Player(1);
var player2 = new Player(2);
var player3 = new Player(3);



player1.setScore(player1.getScore() + 1);
player2.setScore(player2.getScore() + 1);
player3.setScore(player3.getScore() + 1);
<div id="scoreboard"></div>

Upvotes: 2

Related Questions