Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript

Uncaught TypeError: Cannot read property 'drawHTMLToken' of undefined

Hi, I'm having trouble with my code for the 4 in a row challenge, getting the error "Uncaught TypeError: Cannot read property 'drawHTMLToken' of undefined". I looked at all the others with the same issue but found nothing of use.. Anyone know what could be wrong?

class Player { constructor(name, id, color, active = false) { this.name = name; this.id = id; this.color = color; this.active = active; this.tokens = this.createTokens(21); } createTokens(num) { const tokens = [];

  for (let i = 0; i<num.length; i++) {
      let token = new Token(i, this);
      tokens.push(token);
  }

  return tokens;
}

get unusedTokens() {
    return this.tokens.filter(token => !token.dropped);
}

get activeToken() {
   return this.unusedTokens[0];
}

}

const game = new Game();

document.getElementById("begin-game").addEventListener("click", function() { game.startGame(); this.style.display = 'none'; document.getElementById('play-area').style.opacity = '1'; });

class Game { constructor() { this.board = new Board(); this.players = this.createPlayers(); this.ready = false; }

get activePlayer() {
    return this.players.find(player => player.active);
}

createPlayers() {
   const players = [new Player("Player 1", 1, "#e15258", true),
                    new Player("Player 2", 2, "#e59a13")];
    return players;
}  

startGame() {
   this.board.drawHTMLboard();
   this.activePlayer.activeToken.drawHTMLToken();
   this.ready = true;
}

}

class Token { constructor(index, owner) {
this.owner = owner; this.id = token-${index}-${owner.id}; this.dropped = false; } drawHTMLToken() { const token = document.createElement("div"); document.getElementById("game-board-underlay").appendChild(token); token.setAttribute("id", this.id); token.setAttribute("class", "token"); token.style.backgroundColor = this.owner.color; }

get htmlToken() { return this.drawHTMLToken(); } }

class Space { constructor(x, y) { this.x = x; this.y = y; this.id = space-${x}-${y}; this.token = null; this.diameter = 76; this.radius = this.diameter/2; }

drawSVGSpace() {
    const svgSpace = document.createElementNS("http://www.w3.org/2000/svg", "circle");

    svgSpace.setAttributeNS(null, "id", this.id);
    svgSpace.setAttributeNS(null, "cx", (this.x * this.diameter) + this.radius);
    svgSpace.setAttributeNS(null, "cy", (this.y * this.diameter) + this.radius);
    svgSpace.setAttributeNS(null, "r", this.radius - 8);
    svgSpace.setAttributeNS(null, "fill", "black");
    svgSpace.setAttributeNS(null, "stroke", "none");

    document.getElementById("mask").appendChild(svgSpace);   

}

}

class Board { constructor() { this.rows = 6; this.columns = 7; this.spaces = this.createSpaces(); } createSpaces() { const spaces = [];

   for (let x = 0; x < this.columns; x++) {
      const col = [];

       for (let y = 0; y < this.rows; y++) {
           const space = new Space(x, y);
           col.push(space);
       }
       spaces.push(col);
   }
   return spaces;
}

drawHTMLboard() {
    for (let col of this.spaces) {
        for (let space of col) {
            space.drawSVGSpace();
        }
    }
    }
}

Just realized the code wasn't so easy to read, trying to post it all again.

const game = new Game();

document.getElementById("begin-game").addEventListener("click", function() {
    game.startGame();
    this.style.display = 'none';
    document.getElementById('play-area').style.opacity = '1';
});

class Board {
    constructor() {
        this.rows = 6;
        this.columns = 7;
        this.spaces = this.createSpaces();
    }
    createSpaces() {
       const spaces = [];

       for (let x = 0; x < this.columns; x++) {
          const col = [];

           for (let y = 0; y < this.rows; y++) {
               const space = new Space(x, y);
               col.push(space);
           }
           spaces.push(col);
       }
       return spaces;
    }

    drawHTMLBoard() {
        for (let col of this.spaces) {
            for (let space of col) {
                space.drawSVGSpace();
            }
        }
    }
}



class Game {
    constructor() {
        this.board =  new Board();
        this.players = this.createPlayers();
        this.ready = false;
    }

    get activePlayer() {
        return this.players.find(player => player.active);
    }

    createPlayers() {
       const players = [new Player("Player 1", 1, "#e15258", true),
                        new Player("Player 2", 2, "#e59a13")];
       return players;
    }  

    startGame() {
       this.board.drawHTMLBoard();
       this.activePlayer.activeToken.drawHTMLToken();
       this.ready = true;
    }

}


class Player {
    constructor(name, id, color, active = false) {
      this.name = name;
      this.id = id;
      this.color = color;
      this.active = active;
      this.tokens = this.createTokens(21);
    }
    createTokens(num) {
      const tokens = [];

      for (let i = 0; i<num.length; i++) {
          let token = new Token(i, this);
          tokens.push(token);
      }

      return tokens;
    }

    get unusedTokens() {
        return this.tokens.filter(token => !token.dropped);
    }

    get activeToken() {
       return this.unusedTokens[0];
    }
}


class Space {
    constructor(x, y) {
        this.x = x;
        this.y = y;
        this.id = `space-${x}-${y}`;
        this.token = null;
        this.diameter = 76;
        this.radius = this.diameter/2;
    }

    drawSVGSpace() {
        const svgSpace = document.createElementNS("http://www.w3.org/2000/svg", "circle");

        svgSpace.setAttributeNS(null, "id", this.id);
        svgSpace.setAttributeNS(null, "cx", (this.x * this.diameter) + this.radius);
        svgSpace.setAttributeNS(null, "cy", (this.y * this.diameter) + this.radius);
        svgSpace.setAttributeNS(null, "r", this.radius - 8);
        svgSpace.setAttributeNS(null, "fill", "black");
        svgSpace.setAttributeNS(null, "stroke", "none");

        document.getElementById("mask").appendChild(svgSpace);   

    }

}


class Token {
  constructor(index, owner) {  
    this.owner = owner;
    this.id = `token-${index}-${owner.id}`;
    this.dropped = false;
  }
  drawHTMLToken() {
      const token = document.createElement("div");
      token.setAttribute("id", this.id);
      token.setAttribute("class", "token");
      token.style.backgroundColor = this.owner.color;
      document.getElementById("game-board-underlay").appendChild(token);
  }

  get htmlToken() {
    return this.drawHTMLToken();
  }
}

1 Answer

Hi Samuel,

If you’d like to post a link to your repo I can run your code and almost assuredly find out what’s causing your error.

I think I may have spotted what’s going on, but I don’t want to say with 100% certainty that the following is your issue.

Here’s what I think is going on though... In your createTokens function (in the Players class) you’re calling the length property on the num parameter. But you’re passing in a number primitive as the argument. I’m pretty sure that a number doesn’t have a length property. And if I’m right, that’s likely causing your for loop to never actually run. Which would mean that when you return tokens at the end of the createTokens function you’re simply returning an empty array. And so when you try to get the activeToken from an array with no items in it, your activeToken ends up being undefined. And you can’t call drawHTMLToken on a value/object that’s undefined. And so JavaScript raises an error.

Hopefully, that helps you out. If not, again, if you link your repo I’ll be able to actually run your code.

You're spot on, thanks a bunch Brandon! Can't count the times I've gone over the for loop, yet I overlooked this. It makes perfect sense a number can't have a length property.