How to Build Tic Tac Toe Game in JavaScript?

Learning JavaScript by building games is a very effective way to improve your logic and understanding of JavaScript’s interactivity. I am going to show you how to build a simple Tic Tac Toe game using JavaScript. This is the demo of the game we’re going to build I recommend trying the demo out first to know what we’re building since each tutorial’s implementation might differ. For you who prefer to directly see the code, here’s my GitHub repo of the code.

We can breakdown the process into the steps below :

  1. Draw the board
  2. Define the global variables
  3. How the player makes a move
  4. Add the logic for changing turns
  5. Validate the win
  6. Restart the game

1. Draw the board

First things first, we have to build the interface where we are going to play the game. Since we are focusing on the JavaScript, I’m making the HTML and CSS as simple as I can.

Here we are making the basic HTML structure code (including the reference to your CSS and JS file). Then, we make a main tag with the board class to display the game.

Inside the board, we are making the cells where we are placing our Xs and Os. Just leave them empty for now. We are going to fill them with JS. We are also giving them a cell class to style them and data-cell attribute to identify what their index is. (If you’re like me and are confused about what data-cell is, you can learn about it on CSS-Trick’s post about data-* attributes).

One las touch to the HTML, we will add a div to display a message about turns or the game results and also a restart button below it.

Here is the code implementation of my explanation above.

HTML
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css">
    <title>Tic-Tac-Toe</title>
</head>
<body>
    <main class="board">
        <div class="cell" data-cell="0"></div>
        <div class="cell" data-cell="1"></div>
        <div class="cell" data-cell="2"></div>
        <div class="cell" data-cell="3"></div>
        <div class="cell" data-cell="4"></div>
        <div class="cell" data-cell="5"></div>
        <div class="cell" data-cell="6"></div>
        <div class="cell" data-cell="7"></div>
        <div class="cell" data-cell="8"></div>

        <div id="message">X's turn</div>
        <button id="restart">Restart</button>
    </main>
    
    <script src="./script.js"></script>
</body>
</html>

Now that we’ve laid out the HTML, we’re getting into the styling of the game. Here’s the code and I’ll explain directly in the comments of the code.

CSS
/* remove margin so the body has full width */
body {
    margin: 0;
}

/* the styling of the board. Grid to style the 3x3 boxes easily */
.board {
    display: grid;
    width: 100vw;
    height: 100vh;
    grid-template-columns: repeat(3, 100px);
    grid-template-rows: repeat(3, 100px);
    justify-content: center;
    align-content: center;
}

/* the individual boxes where X and O are placed. */
.cell {
    border: 1px solid black;
    display: flex;
    justify-content: center;
    align-items: center;
    font-weight: bold;
    font-size: 50px;
}

/* display the message and restart button. takes 3 grid that's why there are grid colum start and end */
#message, #restart {
    grid-column-start: 1;
    grid-column-end: 4;
    margin: auto;
    margin-top: 10px;
}

/* specifying the size of the message */
#message {
    font-size: xx-large;
    font-weight: bold;
}

2. Define the global variables

Now we are getting into the JavaScript part. We’re defining all the global variables at the top. Here are the variables we need to define.

  • turn : to tell whose turn it is. X’s or O’s
  • cells, message and restart : the elements of the specified selectors
  • steps : to keep track of which cells each player has occupied
  • winConditions : the patterns to win (more on this later)
JavaScript
let turn = 'X';
let cells = document.querySelectorAll('[data-cell]');
let message = document.querySelector('#message');
let restart = document.querySelector('#restart');
let steps = {
    X: [],
    O: []
}
const winConditions = [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [0, 3, 6],
    [1, 4, 7],
    [2, 5, 8],
    [0, 4, 8],
    [2, 4, 6]
];

3. How the player makes a move

Now we’re getting into the fun part, the logic!! It took me some time to figure out these steps. We’re going to figure out how the player can make a move, how they’re gonna place the X and O.

We have to add a listener to each of the cells

JavaScript
cells.forEach(cell => {
    cell.addEventListener("click", () => move(cell) );
});

We’re just looping through the cells we defined before and adding listener to each of them. You noticed there’s a move function there, don’t worry, we’re making it right away.

JavaScript
function move(cell) {
    cell.innerHTML = turn; // marking the cell with X or O based on turn
    cell.style.pointerEvents = 'none'; // making that cell not clickable so it can't be changed

    // getting the index of the cell and pushing it into their respective step tracker
    let cellIndex = parseInt(cell.getAttribute("data-cell"));
    if (turn === 'X') {
        steps.X.push(cellIndex);
    } else {
        steps.O.push(cellIndex);
    }

    // function to change the turn after a move is done. Explained later
    changeTurn();
}

4. Add the logic for changing turns

For this step, there are 2 blocks of code. The changeTurn and stopGame function. I’ll explain the code with comments.

JavaScript
function changeTurn() {
    // check if there is a winner already. Explained later. It returns eaither the winner or null
    let winner = validateWin();

    if (winner) {
        // if there is a winner, stop the game. Function explained later
        stopGame(`${winner}'s win!`);
    } else if (!winner && steps.X.length + steps.O.length === 9) {
        // if the total number of steps is 9, it's a draw, stop the game
        stopGame('Draw!');
    } else {
        // if the game is not done, change the turn, if it;s X, change to O and the reverse
        if (turn === 'X') {
            turn = 'O';
        } else {
            turn = 'X';
        }
        // display whose turn it is
        message.innerHTML = `${turn}'s turn`;
    }
}

function stopGame(msg) {
    message.innerHTML = msg; // display a win or a draw based on the parameter
    // make all cells not clickable
    cells.forEach(cell => {
        cell.style.pointerEvents = 'none';
    });
}

5. Validate the win

Remember the winConditions that we defined earlier, I’ll explain it now. Tow win in tic tac toe, we have to mark 3 cells that make a straight line. If we number the cells like the image below, we can see that to win we need to mark the horizontal straight lines ([0,1,2], [3,4,5], [6,7,8]), the verticals ([0,3,5], [1,4,7], [2,5,8]) and the diagonals ([0,4,8], [2,4,6]). So that’s the contents of winConditions.

Numbered cells

To check if there’s a winner, we use the validateWin function.

JavaScript
function validateWin() {
    let win = null;
    let winner = null;
    if (turn === 'X') {
        win = winConditions.some(winCondition => {
            return winCondition.every(condition => steps.X.includes(condition));
        });
        if (win) winner = 'X';
    } else if (turn === 'O') {
        win = winConditions.some(winCondition => {
            return winCondition.every(condition => steps.O.includes(condition));
        });
        if (win) winner = 'O';
    }
    return winner;
}

To check if there’s a winner, we first check whose turn it is. If it’s X’s turn, we check if X wins. We loop through winConditions and see if there’s at least 1 pattern that matches the steps X has taken (For example, X has taken the cells for [0,4,6,8] and we can see it includes [0,6,8] which matches one of the winConditions). And the same is done if it’s O’s turn. If there’s now winner yet, it’ll just return null.

At this point, your game will be playable already until there’s a win or a draw. Now, we just need to make it able to restart

6. Restart the game

To restart the game, we just need to add a listener to the restart button and a restart function which returns all the global variables to their starting conditions.

JavaScript
restart.addEventListener("click", restartGame);

function restartGame() {
    steps.O = [];
    steps.X = [];
    turn = 'X';
    message.innerHTML = "X's turn";
    cells.forEach(cell => {
        cell.innerHTML = null;
        cell.style.pointerEvents = 'auto';
    });
}

Congrats! Now your game’s done and is completely playable.

Conclusion

Those are the steps and code you can use to build Tic Tac Toe in JavaScript. If you find a better way to do it, do let me know in the comments!

Here a some things you can do to improve the game and your skills

  • Make a 4×4 or 5×5 version
  • Make a version to play with bot
  • Enable choosing player
  • Make a more effective algorithm

If you want to host your result, you can do it on GitHub Pages for free or if you want it to look more professional, you can host it on Cloudways. It is a compatible, fast and reliable managed cloud hosting. Go ahead and check it out!

For you who want to brush up your JavaScript skills, whether to deepen or learn form zero, I recommend you try Codecademy. There are tons of JavaScript lessons that you can learn in a structured and interactive way. You can learn from the most basic materials to advanced stuffs and even preparing for technical interviews. Go try it now!

If you are interested to practice JavaScript by building other games, you can try my other posts :

Now go build your own Tic Tac Toe! What game should I build next?

Leave a Comment