DEV Community

Cover image for Tic Tac Toe 🎮 with HTML, CSS and JS - part 1
Jothin kumar
Jothin kumar

Posted on • Updated on

Tic Tac Toe 🎮 with HTML, CSS and JS - part 1

In this tutorial, we will be creating a basic Tic Tac Toe game with HTML, CSS and JavaScript.

Python version: https://dev.to/jothinkumar/tic-tac-toe-with-python-tkinter-part-1-2gbe

The webpage 👀

Let's go ahead and create a GUI for the game.

Step 1: Create webpage and add some CSS.

  • index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Tic Tac Toe</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <h1>Tic Tac Toe</h1>
    <div id="play-area">
        <button class="square" id="square1"></button>
        <button class="square" id="square2"></button>
        <button class="square" id="square3"></button>
        <br>
        <button class="square" id="square4"></button>
        <button class="square" id="square5"></button>
        <button class="square" id="square6"></button>
        <br>
        <button class="square" id="square7"></button>
        <button class="square" id="square8"></button>
        <button class="square" id="square9"></button>
    </div>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode
  • style.css
body {
    position: absolute;
    text-align: center;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    padding: 10px;
    box-shadow: black 0 0 10px;
}
h1 {
    color: red;
}
#play-area {
    border: black solid 2px;
    overflow: hidden;
}
.square {
    width: 5em;
    height: 5em;
    float: left;
    border: black solid 1px;
    background-color: white;
    cursor: pointer;
}
.square:hover {
    background-color: orange;
    color: white;
}
Enter fullscreen mode Exit fullscreen mode

Tic Tac Toe webpage interface with HTML and CSS
Step 2: Make the webpage functional with JavaScript.

  • script.js
let currentChr = "X";
let XPoint = [];
let OPoint = [];

class XOSquare {
    constructor(x, y, buttonId) {
        this.x = x;
        this.y = y;
        this.button = document.getElementById(buttonId);
        this.button.onclick = () => {
            this.set(buttonId)
        }
    }
    set(buttonId) {
        this.button = document.getElementById(buttonId);
        if (this.button.innerText === "") {
            this.button.innerText = currentChr;
            switchChr();
        }
    }
    reset() {
        this.button.innerText = "";
    }
}
function switchChr() {
    if (currentChr === "X") {
        currentChr = "O";
    } else {
        currentChr = "X";
    }
}
function setup() {
    let squares = [];
    let squareElements = document.getElementsByClassName("square");
    for (let i = 0; i < squareElements.length; i++) {
        let square = new XOSquare(i % 3, Math.floor(i / 3), squareElements[i].id);
        squares.push(square);
    }
}
window.onload = setup;
Enter fullscreen mode Exit fullscreen mode

Add this to index.html under head tag.

<script src="script.js"></script>
Enter fullscreen mode Exit fullscreen mode

Tic Tac Toe functional webpage interface with HTML, CSS and JavaScript

Detect win and draw 🤔

Let's now implement a logic to detect win/draw.

Step 3: Implement logic to detect win.

We need to check after each move if X or O won the game. There are 8 possible ways in which one can win Tic Tac Toe:
8 possible ways of winning Tic Tac Toe
Let's add some the JavaScript in script.js to detect game win.

let currentChr = "X";
let XPoint = [];
let OPoint = [];

class XOSquare {
    constructor(x, y, buttonId) {
        this.x = x;
        this.y = y;
        this.button = document.getElementById(buttonId);
        this.button.onclick = () => {
            this.set(buttonId)
        }
    }
    set(buttonId) {
        this.button = document.getElementById(buttonId);
        if (this.button.innerText === "") {
            this.button.innerText = currentChr;
            if (currentChr === "X") {
                XPoint.push(this);
            } else {
                OPoint.push(this);
            }
            switchChr();
            checkWin();
        }
    }
    reset() {
        this.button.innerText = "";
    }
}
class winningPossibility {
    constructor(x1, y1, x2, y2, x3, y3) {
        this.x1 = x1;
        this.y1 = y1;
        this.x2 = x2;
        this.y2 = y2;
        this.x3 = x3;
        this.y3 = y3;
    }
}
function checkWinningPossibility(winningPossibility, forChr) {
    let p1Satisfied = false;
    let p2Satisfied = false;
    let p3Satisfied = false;
    if (forChr === 'X') {
        for (let i = 0; i < XPoint.length; i++) {
            if (XPoint[i].x === winningPossibility.x1 && XPoint[i].y === winningPossibility.y1) {
                p1Satisfied = true;
            }
            else if (XPoint[i].x === winningPossibility.x2 && XPoint[i].y === winningPossibility.y2) {
                p2Satisfied = true;
            }
            else if (XPoint[i].x === winningPossibility.x3 && XPoint[i].y === winningPossibility.y3) {
                p3Satisfied = true;
            }
        }
    } else {
        for (let i = 0; i < OPoint.length; i++) {
            if (OPoint[i].x === winningPossibility.x1 && OPoint[i].y === winningPossibility.y1) {
                p1Satisfied = true;
            }
            else if (OPoint[i].x === winningPossibility.x2 && OPoint[i].y === winningPossibility.y2) {
                p2Satisfied = true;
            }
            else if (OPoint[i].x === winningPossibility.x3 && OPoint[i].y === winningPossibility.y3) {
                p3Satisfied = true;
            }
        }
    }
    return p1Satisfied && p2Satisfied && p3Satisfied;
}
const winningPossibilities = [
    new winningPossibility(1, 1, 1, 2, 1, 3),
    new winningPossibility(2, 1, 2, 2, 2, 3),
    new winningPossibility(3, 1, 3, 2, 3, 3),
    new winningPossibility(1, 1, 2, 1, 3, 1),
    new winningPossibility(1, 2, 2, 2, 3, 2),
    new winningPossibility(1, 3, 2, 3, 3, 3),
    new winningPossibility(1, 1, 2, 2, 3, 3),
    new winningPossibility(3, 1, 2, 2, 1, 3)
]
function checkWin() {
    for (let i = 0; i < winningPossibilities.length; i++) {
        if (checkWinningPossibility(winningPossibilities[i], 'X')) {
            console.log("X wins");
            return;
        }
        if (checkWinningPossibility(winningPossibilities[i], 'O')) {
            console.log("O wins");
            return;
        }
    }
}
function setup() {
    let squares = [];
    let squareElements = document.getElementsByClassName("square");
    for (let i = 0; i < squareElements.length; i++) {
        let square = new XOSquare(i % 3 + 1, Math.floor(i / 3) + 1, squareElements[i].id);
        squares.push(square);
    }
}
Enter fullscreen mode Exit fullscreen mode

Tic Tac Toe interface with HTML, CSS and JS
When X or O wins the game, console.log is triggered.

Step 4: Detect draw.

Append the following code to function "checkWin" in script.js

if (XPoint.length + OPoint.length === 9) {
    console.log("Draw");
}
Enter fullscreen mode Exit fullscreen mode

Enhancements

Step 5: Add a status label and use it instead of console.log

Let's make a few changes to script.js:

  • function "switchChr":
function switchChr() {
    const statusLabel = document.getElementById("status");
    if (currentChr === "X") {
        currentChr = "O";
        statusLabel.innerText = "O's turn";
    } else {
        currentChr = "X";
        statusLabel.innerText = "X's turn";
    }
}
Enter fullscreen mode Exit fullscreen mode
  • function "checkWin":
function checkWin() {
    const statusLabel = document.getElementById("status");
    for (let i = 0; i < winningPossibilities.length; i++) {
        if (checkWinningPossibility(winningPossibilities[i], 'X')) {
            statusLabel.innerText = "X wins";
            disableGame();
            return;
        }
        if (checkWinningPossibility(winningPossibilities[i], 'O')) {
            statusLabel.innerText = "O wins";
            disableGame();
            return;
        }
    }
    if (XPoint.length + OPoint.length === 9) {
        statusLabel.innerText = "Draw";
        disableGame();
    }
}
Enter fullscreen mode Exit fullscreen mode

Add this new element in index.html under body tag

<p id="status">X's turn</p>
Enter fullscreen mode Exit fullscreen mode

Add the following to style.css

#status {
    color: green;
}
Enter fullscreen mode Exit fullscreen mode

Tic Tac Toe with HTML, CSS and JS with status label
Step 6: Play again

Let's add a play again button so that we don't need to refresh the webpage if we want to replay. We need to create new functions in script.js

  • function "playAgain"
function playAgain() {
    const buttons = document.getElementsByClassName("square");
    for (let i = 0; i < buttons.length; i++) {
        buttons[i].disabled = false;
        buttons[i].innerText = "";
    }
    XPoint = [];
    OPoint = [];
    currentChr = "X";
    const statusLabel = document.getElementById("status");
    statusLabel.innerText = "X's turn";
    const playAgainButton = document.getElementById("play-again");
    playAgainButton.style.display = "none";
}
Enter fullscreen mode Exit fullscreen mode
  • function "disableGame":
function disableGame() {
    const buttons = document.getElementsByClassName("square");
    for (let i = 0; i < buttons.length; i++) {
        buttons[i].disabled = true;
    }
    const playAgainButton = document.getElementById("play-again");
    playAgainButton.style.display = "block";
}
Enter fullscreen mode Exit fullscreen mode

Add this element to index.html under body tag:

<button id="play-again" onclick="playAgain()">Play Again</button>
Enter fullscreen mode Exit fullscreen mode

Add this property to #play-area in style.css:

 margin-bottom: 10px;
Enter fullscreen mode Exit fullscreen mode

Add some css for #play-again (play again button) in style.css:

#play-again {
    box-shadow: black 0 0 5px;
    margin: auto;
    display: none;
}
Enter fullscreen mode Exit fullscreen mode

Step 7: Theme switch.

A webpage won't be complete without a cool theme switch. So, let's add one!

Add the following JS code to script.js:

let currentTheme = 'light';
function switchTheme() {
    if (currentTheme === 'dark') {
        document.querySelectorAll('.dark-mode').forEach(function (element) {
            element.classList.remove('dark-mode');
            element.classList.add('light-mode');
        });
        currentTheme = 'light';
    }
    else {
        document.querySelectorAll('.light-mode').forEach(function (element) {
            element.classList.remove('light-mode');
            element.classList.add('dark-mode');
        });
        currentTheme = 'dark';
    }
}
Enter fullscreen mode Exit fullscreen mode

Let's rewrite the CSS:

body.light-mode {
    position: absolute;
    text-align: center;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    padding: 10px;
    box-shadow: black 0 0 10px;
}
h1.light-mode {
    color: red;
}
#status.light-mode {
    color: green;
}
#play-area.light-mode {
    border: black solid 2px;
    overflow: hidden;
    margin-top: 10px;
    margin-bottom: 10px;
}
.square.light-mode {
    width: 5em;
    height: 5em;
    float: left;
    border: black solid 1px;
    background-color: white;
    cursor: pointer;
}
.square.light-mode:hover {
    background-color: orange;
    color: white;
}
.square.clicked.light-mode {
    background-color: red;
    color: white;
}
#play-again.light-mode {
    box-shadow: black 0 0 5px;
    margin: auto;
    display: none;
}

body.dark-mode {
    position: absolute;
    text-align: center;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    padding: 10px;
    box-shadow: white 0 0 10px;
    background: black;
}
h1.dark-mode {
    color: white;
}
#status.dark-mode {
    color: blue;
}
#play-area.dark-mode {
    border: white solid 2px;
    overflow: hidden;
    margin-top: 10px;
    margin-bottom: 10px;
}
.square.dark-mode {
    width: 5em;
    height: 5em;
    float: left;
    border: white solid 1px;
    background-color: black;
    color: white;
    cursor: pointer;
}
.square.dark-mode:hover {
    background-color: gray;
    color: white;
}
#play-again.dark-mode {
    box-shadow: black 0 0 5px;
    margin: auto;
    display: none;
}
Enter fullscreen mode Exit fullscreen mode

Let's change the body tag in index.html:

<body class="light-mode">
    <h1 class="light-mode">Tic Tac Toe</h1>
    <p id="status" class="light-mode">X's turn</p>
    <button id="theme-switch" onclick="switchTheme()" class="light-mode">Switch Theme</button>
    <div id="play-area" class="light-mode">
        <button class="square light-mode" id="square1"></button>
        <button class="square light-mode" id="square2"></button>
        <button class="square light-mode" id="square3"></button>
        <br>
        <button class="square light-mode" id="square4"></button>
        <button class="square light-mode" id="square5"></button>
        <button class="square light-mode" id="square6"></button>
        <br>
        <button class="square light-mode" id="square7"></button>
        <button class="square light-mode" id="square8"></button>
        <button class="square light-mode" id="square9"></button>
    </div>
    <button id="play-again" onclick="playAgain()" class="light-mode">Play Again</button>
</body>
Enter fullscreen mode Exit fullscreen mode

Tic Tac Toe with HTML, CSS and JS theme switch

Full code available at GitHub repository: https://github.com/Jothin-kumar/tic-tac-toe

If you find this article useful, drop a like ⭐ and follow me to get all my latest content.

Top comments (0)