How to Make a Simple Tic-Tac-Toe Game with HTML, CSS, and JavaScript

In this blog, we will see how to create a simple Tic-Tac-Toe game using just HTML, CSS, and JavaScript. This is a fun little project that will help us understand how JavaScript interacts with HTML and CSS to create interactive websites.

Let’s break it down into three parts:

1. HTML (Structure of the Game)

The HTML part is simple. It creates the layout for the game, i.e., the Tic-Tac-Toe board and a button to restart the game.

<!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</title>
    <link rel="stylesheet" href="styles.css">
</head>
<body>
    <div>
        <h1>Tic-Tac-Toe</h1>
        <div id="board">
            <!-- These divs create the Tic-Tac-Toe board with 9 cells -->
            <div data-cell></div>
            <div data-cell></div>
            <div data-cell></div>
            <div data-cell></div>
            <div data-cell></div>
            <div data-cell></div>
            <div data-cell></div>
            <div data-cell></div>
            <div data-cell></div>
        </div>
        <h2 id="statusMessage"></h2> <!-- To display game status like "X's turn" or "O's win" -->
        <button id="restartButton">Restart Game</button> <!-- Button to restart the game -->
    </div>
    <script src="script.js"></script> <!-- Linking JavaScript file -->
</body>
</html>

The HTML file is basically setting up the skeleton of the game. It includes the game board (which is made up of 9 cells), a status message, and a button to restart the game. We also link the external CSS file (styles.css) and JavaScript file (script.js).

2. CSS (Styling the Game)

The CSS file is used to make the game look nice. It defines how the board, cells, and buttons should appear on the screen.

* {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}
body {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh;
    font-family: Arial, sans-serif;
    background-color: #f4f4f4;
}
.game-container {
    text-align: center;
}
.board {
    display: grid;
    grid-template-columns: repeat(3, 100px); /* Creates 3 columns for the game board */
    grid-template-rows: repeat(3, 100px); /* Creates 3 rows for the game board */
    gap: 10px; /* Space between the cells */
    margin: 20px auto;
}
.cell {
    width: 100px;
    height: 100px;
    background-color: #fff;
    border: 2px solid #333;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 2rem;
    cursor: pointer; /* Makes the cells clickable */
    color: #333;
}
h2 {
    margin: 20px 0;
    color: #333;
}
button {
    padding: 10px 20px;
    background-color: #007BFF;
    color: white;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    font-size: 1rem;
}
button:hover {
    background-color: #0056b3;
}

The CSS part is used to design the layout of the game. It uses a grid layout to structure the game board with 3×3 cells. It also styles the button and status message for better user experience.

3. JavaScript (Game Logic)

Now comes the main part—the JavaScript code that controls the game logic. This part handles how the game behaves, like switching turns between X and O, checking for wins, and resetting the game.

// Defining classes for X and O players
const X_CLASS = 'x';
const O_CLASS = 'o';
// Selecting all the cells and other elements by their IDs or data attributes
const cellElements = document.querySelectorAll('[data-cell]');
const board = document.getElementById('board');
const statusMessage = document.getElementById('statusMessage');
const restartButton = document.getElementById('restartButton');
// Turn indicator (false for X's turn, true for O's turn)
let oTurn;
// Possible winning combinations (rows, columns, diagonals)
const WINNING_COMBINATIONS = [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [0, 3, 6],
    [1, 4, 7],
    [2, 5, 8],
    [0, 4, 8],
    [2, 4, 6]
];
// Function to start the game
startGame();
// Event listener for restarting the game
restartButton.addEventListener('click', startGame);
// Start or reset the game
function startGame() {
    oTurn = false; // Start with X's turn
    cellElements.forEach(cell => {
        cell.classList.remove(X_CLASS); // Clear X's mark
        cell.classList.remove(O_CLASS); // Clear O's mark
        cell.textContent = ''; // Clear text in each cell
        cell.removeEventListener('click', handleClick); // Remove previous event listeners
        cell.addEventListener('click', handleClick, { once: true }); // Add new event listener
    });
    setStatusMessage();
}
// Handle cell click
function handleClick(e) {
    const cell = e.target;
    const currentClass = oTurn ? O_CLASS : X_CLASS; // Set current class based on turn
    placeMark(cell, currentClass); // Place X or O in the clicked cell
    if (checkWin(currentClass)) {
        endGame(false); // End the game if someone wins
    } else if (isDraw()) {
        endGame(true); // End the game if it's a draw
    } else {
        swapTurns(); // Switch turns
        setStatusMessage();
    }
}
// Place X or O in the cell
function placeMark(cell, currentClass) {
    cell.classList.add(currentClass); // Add X or O class to the cell
    cell.textContent = currentClass === X_CLASS ? 'X' : 'O'; // Display X or O
}
// Swap turns between X and O
function swapTurns() {
    oTurn = !oTurn; // Switch the boolean value to alternate between X and O
}
// Check for a win based on the current class (X or O)
function checkWin(currentClass) {
    return WINNING_COMBINATIONS.some(combination => {
        return combination.every(index => {
            return cellElements[index].classList.contains(currentClass); // Check if all cells in a winning combination contain the same class
        });
    });
}
// Check if all cells are filled without a winner (draw)
function isDraw() {
    return [...cellElements].every(cell => {
        return cell.classList.contains(X_CLASS) || cell.classList.contains(O_CLASS); // Check if every cell is filled
    });
}
// End the game and show message
function endGame(draw) {
    if (draw) {
        statusMessage.innerText = "It's a Draw!";
    } else {
        statusMessage.innerText = `${oTurn ? "O's" : "X's"} Wins!`;
    }
}
// Update the status message to show whose turn it is
function setStatusMessage() {
    statusMessage.innerText = `${oTurn ? "O's" : "X's"} Turn`;
}

The JavaScript part controls the game. It handles switching between turns, checks for winners, detects a draw, and displays the game’s status (e.g., whose turn it is). It also allows restarting the game by clearing the board and starting fresh.

Final Thoughts:

That’s it! Now we have a basic Tic-Tac-Toe game up and running using HTML, CSS, and JavaScript. This is a great little project to practice our JavaScript skills and understand how front-end web development works. Play around with the code and maybe add more features like keeping score or adding a timer for each turn!! 😄

Leave a Comment