How to Create a Tic Tac Toe Game in Javascript.
Tags: webdev, javascript, beginners, programming
Published: 2023-11-06 09:53:08

A Tic Tac Toe game is a fun project to learn Frontend development and Javascript. In this tutorial, I will walk you through step by step on how to build a simple version of it.

Like most front-end development projects, your project should have an HTML file since this is the skeleton of a webpage. Then we add CSS that styles the HTML and finally we add interactivity by incorporating Javascript logic.


So let’s start by setting up HTML!

**HTML**

Set up your HTML file to include all the nine squares of the game.

Add classes and IDs since they will be used in CSS to style them and also in Javascript to add interactivity.

Don’t forget to link the CSS file in the **title tag** and the Javascript file just before the **closing tag** of the body element.<br></br>
**Below is my code for HTML:**

```HTML

<!doctype html>
<html lang="en">
 <head>
   <meta charset="UTF-8" />
   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
   <title>Tic tac toe Project</title>
   <link rel="stylesheet" href="index.css" />
    </head>
 <body>
   <h1>Tic Tac-Toe Project</h1>

   <section class="game-board-container scroll-container">
     <article class="game-board">
       <div class="sqrs" id="1">1</div>
       <div class="sqrs" id="2">2</div>
       <div class="sqrs" id="3">3</div>
       <div class="sqrs" id="4">4</div>
       <div class="sqrs" id="5">5</div>
       <div class="sqrs" id="6">6</div>
       <div class="sqrs" id="7">7</div>
       <div class="sqrs" id="8">8</div>
       <div class="sqrs" id="9">9</div>
     </article>

     <article class="form-sect">
       <form>
         <label for="player1">Player1 Name</label>
         <input type="text" id="player1" required />
         <label for="player2">Player2 Name</label>
         <input type="text" id="player2" required />
         <input type="submit" value="Continue" id="submit" />
       </form>
     </article>

     <article class="display-info"></article>
   </section>
   <script src="index.js"></script>

 </body>
</html>



```

The code above has a form, that is not necessary but you may want to add it since it allows players to submit their names before the game begins and also it allows random selection of the first player to start.
Our project should look like this:

![tic tac toe photo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z9otdrioym4rbglk60cc.png)

## **CSS**

Without CSS the project would look ugly.

It is time we make our project appealing.

Just as a reminder, to style elements in CSS we use **CSS selectors.**

To access class elements we start with **.Classname**, to select elements with ids we start with **#idname**.

You can also just select the element directly ie **body**.

**Here is the CSS code for my HTML above:**

```CSS
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}


html {
  overflow-x: hidden;
  max-width: 100%;
  padding-right: 0px !important;
  padding-left: 0px !important;
}

body {
  font-family: "Lucida Sans", "Lucida Sans Regular", "Lucida Grande",
    "Lucida Sans Unicode", Geneva, Verdana, sans-serif;
  font-weight: 500;
  overflow-x: hidden;
  width: 100%;
}

body h1 {
  text-align: center;
  margin-top: 2rem;
}

input,
label {
  display: block;
  font-size: larger;
  margin-top: 1rem;
}

.game-board-container {
  margin: 2rem auto;

  width: 400px;
  display: grid;
}
.form-sect {
  background: green;
  padding: 1rem;
  width: 50%;
  display: none;
  height: auto;
}

.game-board {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 2px;
  height: 50vh;

}

.sqrs {
  background: steelblue;
  display: grid;
  justify-content: center;
  align-items: center;
  cursor: pointer;
  font-size: 30px;
}

.sqrs:hover {
  background: rgb(224, 220, 220);
}

.selected {
  pointer-events: none;
  background: rgb(224, 220, 220);
}

input {
  padding: 0.5rem;
  border: none;
  border-radius: 10px;
}

.display-info {
  margin: 5rem auto;
}

.display {
  width: 100%;
  height: 300px;
  padding: 2rem;
  font-size: 18px;
  font-family: Arial, Helvetica, sans-serif;
  display: grid;

  justify-content: center;
  align-items: center;
  background: green;
  color: rgb(8, 5, 5);
}

.restart {
  font-size: 50px;
  width: 200px;
  padding: 0.7rem;
  border-radius: 15px;
  border: transparent;
  background: white;
}
.display {
  width: 100%;
  height: 300px;
  padding: 2rem;
  font-size: 18px;
  font-family: Arial, Helvetica, sans-serif;
  display: grid;

  justify-content: center;
  align-items: center;
  background: green;
  color: rgb(8, 5, 5);
}

.restart {
  font-size: 50px;
  width: 200px;
  padding: 0.7rem;
  border-radius: 15px;
  border: transparent;
  background: white;
}


```


To make our project responsive on both desktop and mobile phones, we add media queries.

Take note of how to use max-width and min-width in media queries.

**Below is how I implemented this:**

```CSS

@media only screen and (max-width: 500px) {
  .game-board-container {
    position: relative;
    width: 100%;
  }

  .game-board {
    grid-template-columns: repeat(3, 1fr);
    gap: 2px;
    padding: 2rem;
    height: 30vh;
  }

  .form-sect {
    background: green;
    padding: 1rem;
    width: 100%;

    height: auto;
  }

  .form-sect form {
    padding: 2rem;
    background: red;
    width: 100%;
  }

  input {
    padding: 0.9rem;
    margin: 1rem;
    border: none;
    width: 100%;
    border-radius: 10px;
  }
}
@media only screen and (min-width: 500px) {
  .game-board-container {
    width: 100%;
    position: relative;
  }
  .game-board {
    margin-top: 8rem;
    grid-template-columns: repeat(3, 1fr);
    gap: 5px;
    height: 50vh;
    padding: 2rem;
  }

  .form-sect {
    margin: 5rem auto;
    background: green;
    padding: 1rem;
    width: 50%;

    height: auto;
  }

  .form-sect form {
    padding: 2rem;
    background: red;
    width: 100%;
  }

  input {
    padding: 0.9rem;
    margin: 1rem;
    border: none;
    width: 100%;
    border-radius: 10px;
  }
}

@media only screen and (min-width: 900px) {
  .game-board-container {
    width: 100%;
    position: relative;
  }
  .game-board {
    margin: 4rem auto;
    width: 50%;

    gap: 5px;
    padding: 2rem;
  }
}

@media only screen and (min-width: 2000px) {
  .game-board-container {
    width: 50%;
    position: relative;
  }

  .game-board {
    margin-top: 4rem;

    gap: 5px;
    padding: 4rem;
    height: 100vh;
  }

  .sqrs {
    font-size: 50px;
  }


```

By this time the project looks this way:



![Tic-tac-toe-css photo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vueynlc253am4zi4imld.png)

Note that  we have set **.form-sect**  element to display **none**.We will set it to display **grid** after we finish the project so that the form shows up everytime we want to start a new game. Also, we have set **.game-board** to display **grid**, we will change it to display **none** after we finish the project.


## **JAVASCRIPT**

This is the hardest part of the project.
Try to write javascript code that does the following and if you fail come back we walk through together

1. On clicking a square, the inner text changes to either X or O
2. A square cannot be clicked again after initial click
3. Write a function that stores information about the current player and triggers relevant functions to display win, draw or a turn for another player(You can use factory functions)
4. Write a function that changes turn from the current player to the next player.
5. Track the number of squares selected(already clicked) and if no winner is found declare a draw
6. Write a function that tracks the winner, it should have if…else statements to take care of all the possible outcomes i.e columns or rows aligning with the same sign(X or O)
7. Display the winner or a draw accordingly
8. Lastly, you can extend this functionality to create a more complex game.
9. If feeling more ambitious you can create with an unbeatable AI using the min-max function.

Let’s start by fetching all the squares from the DOM so that we can add click event listener to them.<br></br>
Just as reminder, to select more than two elements from the DOM we use **querySelectorAll** while to select only one we use **querySelector.**

`let sqrs = document.querySelectorAll(".sqrs");`

Next we select **all** the squares again but this time we select **one by one**.<br></br>
We will use them in the if..else conditions to check if square one and square two and square three… were clicked by the same player and if they match we declare a win.<br></br>
**The code below illustrates this:**

```javascript
let sqr1 = document.getElementById("1");
let sqr2 = document.getElementById("2");
let sqr3 = document.getElementById("3");
let sqr4 = document.getElementById("4");
let sqr5 = document.getElementById("5");
let sqr6 = document.getElementById("6");
let sqr7 = document.getElementById("7");
let sqr8 = document.getElementById("8");
let sqr9 = document.getElementById("9");
```

We then select other DOM elements and also declare new variables that we may need

```javascript
let player1Name = "";
let player2Name = "";
let selectedSquares = [];
let formSect = document.querySelector(".form-sect");
let gameboard = document.querySelector(".game-board");
let formEl = document.querySelector("form");
let displayInfo = document.querySelector(".display-info");
```

It is time we write a very important **object** (a factory function) that keeps track of everything that goes on in the game.

- A factory function returns a new object.
- This may be the hardest part but don’t worry,I will try to make you understand.
- Let’s name this function **Player(it is actually an object)**.This object is dynamic and stores data of the current and previous player i.e serves each player when their turn comes up. Hence it takes an argument of the name **player**. The value of player changes every time we **change turn**. i.e if the current player is **player1** the next player will be **player2** and invokes the same functions on the player2 as it did on player1.
- The Player object will have a **changeTurn()** function that changes the value of the player argument every **instance** of the game.

  ```javascript
  let playerNumber = player;

  //Change turn function changes the current player to another player after each move.

  changeTurn = function () {
    if (playerNumber == "player1") {
      playerNumber = "player2";
    } else if (playerNumber == "player2") {
      playerNumber = "player1";
    }
  };
  ```

  The other most important function in Player object is the **gameData()** function.

  This function is the one that keeps track of which squares have been clicked, changes innerText of the squares, invokes the **checkwin()** every time a move is made etc.

  This function is returned from the Player object and is available globally so that it can be invoked when the game start (After submission of names in the form).

  Refer to the code below to understand. It comprises of all the code in the Player object

  ```javascript

  const Player = (player) => {
  //Initialize type of player so we can use later to locate the current player and change turns.
  let playerNumber = player;

  //Change turn function changes the current player to another player after each move.

  changeTurn = function () {
    if (playerNumber == "player1") {
      playerNumber = "player2";
    } else if (playerNumber == "player2") {
      playerNumber = "player1";
    }
  };

  /*     ==============form el====================
   */

  //Main function and most important
  //Keeps track of everything that goes in on the game board and responds accordingly

  gameData = function () {
    //console.log(playerNumber);

    sqrs.forEach((sqr) => {
      sqr.addEventListener("click", () => {
        sqr.classList.add("selected");
        selectedSquares.push(sqr.id);
        //Changes sign on the squares when clicked, to  either X or O
        if (playerNumber == "player1") {
          sqr.innerHTML = "X";

          if (checkWin() === "X") {
            displayInfo.style.display = "block";
            gameboard.style.display = "none";
            displayInfo.innerHTML += `
            <div class="display"><h2> ${player1Name} has won</h2>
            <button class="restart">Restart</button>
            </div>
            
            `;

            //Restart button

            document.querySelector(".restart").addEventListener("click", () => {
              location.reload();
            });
          } else if (selectedSquares.length == 9 && checkWin() == false) {
            displayInfo.style.display = "block";
            gameboard.style.display = "none";
            displayInfo.innerHTML += `<div class="display">
            <h2> It is a draw!</h2>
            <button class="restart">Restart</button>
            </div>`;

            //Restart button

            document.querySelector(".restart").addEventListener("click", () => {
              location.reload();
            });
          }

          changeTurn();
        } else if (playerNumber == "player2") {
          sqr.innerHTML = "O";
          if (checkWin() === "O") {
            displayInfo.style.display = "block";
            gameboard.style.display = "none";
            displayInfo.innerHTML += `<div class="display">
            <h2> ${player2Name} has won</h2>
            <button class="restart">Restart</button>
            </div>`;

            //Restart button

            document.querySelector(".restart").addEventListener("click", () => {
              location.reload();
            });
          } else if (selectedSquares.length == 9 && checkWin() == false) {
            displayInfo.style.display = "block";
            gameboard.style.display = "none";
            displayInfo.innerHTML += `<div class="display"><h2> It is a draw!</h2>
             <button class="restart">Restart</button>
            </div>`;

            //Restart button

            document.querySelector(".restart").addEventListener("click", () => {
              location.reload();
            });
          }

          changeTurn();
        }
      });
    });
  };

  return { gameData };
};




  ```

  The next thing that we need to take care of is to check win.

  We will create a global function and then invoke it where appropriate in the **gameData().**

```javascript
const checkWin = () => {
  if (sqr1.innerHTML == sqr2.innerHTML && sqr2.innerHTML == sqr3.innerHTML) {
    return sqr1.innerHTML;
  }
  if (sqr1.innerHTML == sqr4.innerHTML && sqr4.innerHTML == sqr7.innerHTML) {
    return sqr1.innerHTML;
  }
  if (sqr1.innerHTML == sqr5.innerHTML && sqr5.innerHTML == sqr9.innerHTML) {
    return sqr1.innerHTML;
  }
  if (sqr2.innerHTML == sqr5.innerHTML && sqr5.innerHTML == sqr8.innerHTML) {
    return sqr2.innerHTML;
  }
  if (sqr3.innerHTML == sqr6.innerHTML && sqr6.innerHTML == sqr9.innerHTML) {
    return sqr3.innerHTML;
  }
  if (sqr3.innerHTML == sqr5.innerHTML && sqr5.innerHTML == sqr7.innerHTML) {
    return sqr3.innerHTML;
  }
  if (sqr4.innerHTML == sqr5.innerHTML && sqr5.innerHTML == sqr6.innerHTML) {
    return sqr4.innerHTML;
  }
  if (sqr7.innerHTML == sqr8.innerHTML && sqr8.innerHTML == sqr9.innerHTML) {
    return sqr7.innerHTML;
  }

  //If all of the above conditions are not met, then the game continues or a draw  is displayed

  return false;
};
```

The following code is optional but it will be in synchrony with the other code above if you copy pasted or used most of my code, it is just a form and what happens after its submission.

```javascript
/* ==========================Form info================= */

formEl.addEventListener("submit", function (event) {
  event.preventDefault();

  player1Name = document.getElementById("player1").value;
  player2Name = document.getElementById("player2").value;
  console.log(player1Name);

  //Random player to start

  let randomNumber = Math.round(Math.random() * 1);
  let selectedPlayer = "";
  randomNumber == 0
    ? (selectedPlayer = "player1")
    : (selectedPlayer = "player2");

  formSect.style.display = "none";
  displayInfo.style.display = "block";

  selectedPlayer === "player1"
    ? (displayInfo.innerHTML = `<div class="display"><h2>${player1Name} to start</h2></div>`)
    : (displayInfo.innerHTML = `<div class="display"><h2>${player2Name} to start</h2></div>`);

  setTimeout(function () {
    displayInfo.innerHTML = "";
    displayInfo.style.display = "none";
    gameboard.style.display = "grid";
  }, 1500);

  let player = Player(selectedPlayer);
  player.gameData();
});
```

**Congratulations!**

I hope you have learnt a lot from this project.

If for some reasons you haven’t, don’t worry, it may take a while before some concepts sink in.

I wish you well in your web development journey.

[Twitter](https://x.com/Sneviy)
[LinkedIn](https://www.linkedin.com/in/nevily-simiyu/)