“This is the 19th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021”

🌊 Author’s home page: Haiyun 🌊 Author profile: 🏆CSDN full stack quality creator, 🥇HDZ core group member 🌊 Fan benefits: fans send four books every week, a variety of small gifts every month

Creating games in javascript is the most fun way to learn. It will keep you motivated, which is crucial for learning complex skills like Web development. In addition, you can play with your friends, or just show them the little things you make, they will also feel very interesting. In today’s post, we’ll create a tic-tac-toe game using HTML, CSS, and Javascript.

Site: Haiyong. site/xxoo2

The HTML

First in the Head section, I’ll include the CSS and javascript files we’ll create later. I also added a Google font called Itim.

<link rel="stylesheet" href="style.css">
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Itim&display=swap" rel="stylesheet">
<script src="index.js"></script>
Copy the code

The body of the HTML will be fairly simple. To wrap everything, I’ll use a main tag and apply a class background to it. Inside the main wrapper, we’ll have five sections.

The first part will contain only our heading h1.

The second part shows whose turn it is now. In the display, we have a span that contains either X or O depending on the current user. We apply the class to this span to color the text.

The third part is holding the game board. It has a Container class so we can place the tiles properly. In this section, we have nine divs that will act as tiles within the board.

Part four will be responsible for announcing the final competition results. It is empty by default, and we will modify its contents from javascript.

The last section will save our control, which contains a restart button.

<main class="background">
        <section class="title">
            <h1>Tic tac toe</h1>
        </section>
        <section class="display">The player<span class="display-player playerX">X</span>The round</section>
        <section class="container">
            <div class="tile"></div>
            <div class="tile"></div>
            <div class="tile"></div>
            <div class="tile"></div>
            <div class="tile"></div>
            <div class="tile"></div>
            <div class="tile"></div>
            <div class="tile"></div>
            <div class="tile"></div>
        </section>
        <section class="display announcer hide"></section>
        <section class="controls">
            <button id="reset">Start all over again</button>
        </section>
    </main>
Copy the code

Add CSS

I won’t go through every single line of CSS, but you can look at the full code in the source code.

First, I’ll create the style.css file and remove any browser-defined margins and padding, and set the Google font I included in the HTML for the entire document.

* {
    padding: 0;
    margin: 0;
    font-family: 'Itim', cursive;
}
Copy the code

The next important thing we had to add was the style of our board. We will use CSS grids to create boards. We can split the container in half by providing 3 times 33% space for columns and rows. Margin: 0 auto; margin: 0 auto; .

.container {
    margin: 0 auto;
    display: grid;
    grid-template-columns: 33% 33% 33%;
    grid-template-rows: 33% 33% 33%;
    max-width: 300px;
}
Copy the code

Next, we will add the style of the tiles inside the board. We’ll apply a small white border and set the minimum width and height to 100 pixels. We’ll use Flexbox’s and Settings for context-content and align-items to center. We’re going to give it a big font size and apply cursor: pointer so the user knows that this field is clickable.

.tile {
    border: 1px solid white;
    min-width: 100px;
    min-height: 100px;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 50px;
    cursor: pointer;
}
Copy the code

I used two different colors to better distinguish the two players. To do this, I create two utility classes. Player X’s color is green, and player O’s color is blue.

.playerX {
    color: #09C372;
}

.playerO {
    color: #498AFB;
}
Copy the code

Implement Javascript partly because we include Javascript files in . This is necessary because our script will load before the browser parses the HTML body. If you don’t want to include everything in this function, feel free to add defer to the script tag or move the script tag to the body.

window.addEventListener('DOMContentLoaded'.() = >{});Copy the code

First, we’ll save the reference to the DOM node. We will use the document. QuerySelectorAll (). We want an Array, but this function returns a NodeList, so we must use array.from (). We also get references to the player display, reset button, and announcer.

const tiles = Array.from(document.querySelectorAll('.tile'));
const playerDisplay = document.querySelector('.display-player');
const resetButton = document.querySelector('#reset');
const announcer = document.querySelector('.announcer');
Copy the code

Next, we’ll add the global variables needed to control the game. We will initialize a board with an array of nine empty strings. This will save the X ABd O value for each plot block on the board. We’ll have a currentPlayer that holds the flag of the player active in the current round. The isGameActive variable will remain true until someone wins or the game ends in a tie. In these cases, we set it to false so that the remaining graph blocks remain inactive until reset. We have three constants that represent game over states. We use these constants to avoid spelling mistakes.

let board = [' '.' '.' '.' '.' '.' '.' '.' '.' '];
let currentPlayer = 'X';
let isGameActive = true;

const PLAYERX_WON = 'PLAYERX_WON';
const PLAYERO_WON = 'PLAYERO_WON';
const TIE = 'TIE';
Copy the code

In the next step, we will store all winning positions on the board. In each subarray, we will store indexes of the three positions that can win the match. So this [0, 1, 2] will represent the situation where the first horizontal line is occupied by the player. We will use this array to determine if we have a winner.

/* Indexes within the board [0] [1] [2] [3] [4] [5] [6] [7] [8] */

const winningConditions = [
   [0.1.2],
   [3.4.5],
   [6.7.8],
   [0.3.6],
   [1.4.7],
   [2.5.8],
   [0.4.8],
   [2.4.6]].Copy the code

Now we’ll write some utility functions. In the isValidAction function, we will decide whether the user wants to perform a valid action. If the tile’s internal text is XorO we return false as the operation is invalid, otherwise the tile is empty so the operation is valid.

const isValidAction = (tile) = > {
    if (tile.innerText === 'X' || tile.innerText === 'O') {return false;
    }

    return true;
};
Copy the code

The next utility function is going to be very simple. In this function, we will take an index as an argument and set the corresponding element in the checkerboard array to our current player’s symbol.

const updateBoard =  (index) = > {
   board[index] = currentPlayer;
}
Copy the code

We’ll write a small function to handle the player changes. In this function, we’ll start with the template text player${currentPlayer} from playerDisplay. The string will become playerX or playerO depending on the currentPlayer. Next, we will use a ternary expression to change the value of the current player. If it’s X, it’s going to be O otherwise it’s going to be X. Now that we’ve changed our user values, we need to update the innerText playerDisplay and apply the new player class.

const changePlayer = () = > {
    playerDisplay.classList.remove(`player${currentPlayer}`);
    currentPlayer = currentPlayer === 'X' ? 'O' : 'X';
    playerDisplay.innerText = currentPlayer;
    playerDisplay.classList.add(`player${currentPlayer}`);
}
Copy the code

Now we’ll write the announer function that announces the final game result. It receives the end game type and the innerText updates the announcer DOM node based on the result. In the last line, we have to remove the hidden class because the announcer is hidden by default until the end of the game.

const announce = (type) = > {
    switch(type){
       case PLAYERO_WON:
            announcer.innerHTML = 'Player <span class="playerO">O</span> Won';
            break;
       case PLAYERX_WON:
            announcer.innerHTML = 'Player <span class="playerX">X</span> Won';
            break;
       case TIE:
            announcer.innerText = 'Tie';
        }
    announcer.classList.remove('hide');
};
Copy the code

Next we’ll write one of the most interesting parts of the project — the results assessment. First, we will create a roundWon variable and initialize it to false. We will then iterate through the winConditions array and check each winning condition on the board. For example, in the second iteration, we will check the values: board3, board4, board5.

We’ll also do some optimizations, and if any of the fields are empty, we’ll call continue and skip to the next iteration, because you won’t win if there’s an empty graph block in the win condition. If all fields are equal, then we have a winner, so we set roundWon to true and break the for loop because any further iterations would waste the calculation.

After the loop, we will check the value of the roundWon variable, and if true, we will declare the winner and set the game to inactive. If we don’t have a winner, we will check if there are any empty cards on the board, if we don’t have a winner and there are no empty cards, we will declare a tie.

function handleResultValidation() {
  let roundWon = false;
  for (let i = 0; i <= 7; i++) {
    const winCondition = winningConditions[i];
    const a = board[winCondition[0]].const b = board[winCondition[1]].const c = board[winCondition[2]].if (a === "" || b === "" || c === "") {
      continue;
    }
    if (a === b && b === c) {
      roundWon = true;
      break; }}if (roundWon) {
    announce(currentPlayer === "X" ? PLAYERX_WON : PLAYERO_WON);
    isGameActive = false;
    return;
  }

  if(! board.includes("")) announce(TIE);
}
Copy the code

Next we’ll deal with the user’s actions. This function takes a tile and an index as arguments. This function is called when the user clicks on a graph block. First we need to check if it is a valid action, and we will also check if the game is currently active. If both are true, we update the tile’s innerText with the current player’s symbol, add the corresponding class, and update the board array. Now that everything is updated, we have to check to see if the game is over, so we call handleResultValidation(). Finally, we must call the changePlayer method to pass the round to another player.

const userAction = (tile, index) = > {
  if (isValidAction(tile) && isGameActive) {
    tile.innerText = currentPlayer;
    tile.classList.add(`player${currentPlayer}`); updateBoard(index); handleResultValidation(); changePlayer(); }};Copy the code

In order for the game to work, we must add event listeners to the tiles. We can do this by looping through an array of graph blocks and adding an event listener to each graph block. (For better performance, we can only add an event listener to the container and use event bubbles to capture tile clicks on the parent, but I think this is easier to understand for beginners.)

tiles.forEach( (tile, index) = > {
    tile.addEventListener('click'.() = > userAction(tile, index));
});
Copy the code

We only missed one feature: resetting the game. To do this, we’ll write a resetBoard function. In this function, we set the board X to nine empty strings, set the game to active, remove the announcer and change the player back (X always starts by definition).

The last thing we have to do is walk through the block and set the innerText back to an empty string, removing any player-specific classes from the block.

const resetBoard = () = > {
    board = [' '.' '.' '.' '.' '.' '.' '.' '.' '];
    isGameActive = true;
    announcer.classList.add('hide');

    if (currentPlayer === 'O') {
        changePlayer();
    }

    tiles.forEach(tile= > {
        tile.innerText = ' ';
        tile.classList.remove('playerX');
        tile.classList.remove('playerO');
    });
}
Copy the code

Now we just need to register this function as a click event handler for the reset button.

resetButton.addEventListener('click', resetBoard);
Copy the code

That’s it, we have a fully functional tic-tac-toe game that you can play with your friends and have fun with.

🥇 comments section draw fans to send books

💌 welcomes your comments and suggestions in the comments section! (Two lucky winners will send the book, the picture is below)💌

Vue.js Framework and Web Front-end Development from Beginner to Master

Link below to enter the lucky draw:

Wallet.hdcj.zhunzha.com/h5/#/jumpMp…

Write it at the end

The author is determined to build a fishing site with 100 small games, update progress: 41/100

I’ve been writing tech blogs for a long time, mostly through nuggets, and this is my post on creating a simple tic-tac-toe game using HTML, CSS, and JavaScript. I like to share technology and happiness through articles. You can visit my blog at juejin.cn/user/204034… For more information. Hope you like it! 😊

💌 welcomes your comments and suggestions in the comments section! 💌