Hello geeks we are going to make a simple Tic Tac Toe project with the magic of some Confetti animation to make your project stand out amongst all Tic Tac Toe projects.
Play it yourself (https://harshkumar123456.github.io/Tic-Tac-Toe-Using-HTML-CSS-and-JavaScript/)
We are going to use simply HTML, CSS and JavaScript for our project to make it simple. If you are an expert then you can always opt for some framework like React, Angular or Vue or any other to do the same.
Let's start....
First of all create a folder in which you are going to put this project. Let us name that folder project. Open that folder in VS Code(A code editor).
Step 1: Make a index.html file in that folder
Step 2: Now paste that code in your index.html and this is simply starting code of any html file you will be making.
<!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>
</head>
<body>
</body>
</html>
Step 3: Now as we know the tic tac toe game have 9 boxes in it so we have to create them also. Make 9 boxes of tic tac toe and take them inside a div tag and give them appropriate class and id for selecting them in later styling.
<!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>
</head>
<body>
<div class="game-box">
<div class="box" id="1"></div>
<div class="box" id="2"></div>
<div class="box" id="3"></div>
<div class="box" id="4"></div>
<div class="box" id="5"></div>
<div class="box" id="6"></div>
<div class="box" id="7"></div>
<div class="box" id="8"></div>
<div class="box" id="9"></div>
</div>
</body>
</html>
Step 4: Style them as you want here is one such example styling.
<!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>
<style>
/* For Resetting any of the default margin padding in components */
* {
margin: 0px;
padding: 0px;
}
/* For Centering the game-box div and giving some font-family for a different look */
body {
height: 90vh;
padding: 10px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
gap: 20px;
font-family: cursive, 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
}
/* For letting the 9 boxes of tic tac toe in layout of game */
.game-box {
width: 320px;
display: grid;
gap: 10px;
grid-template-rows: 1fr 1fr 1fr;
grid-template-columns: 1fr 1fr 1fr;
}
/* Box styling and making it flex for letting any O or X in center */
.box {
width: 100px;
height: 100px;
border-radius: 18px;
background: #ffffff;
box-shadow: 10px 10px 20px #c7c7c7,
-10px -10px 20px #ffffff;
display: flex;
justify-content: center;
align-items: center;
}
</style>
</head>
<body>
<div class="game-box">
<div class="box" id="1"></div>
<div class="box" id="2"></div>
<div class="box" id="3"></div>
<div class="box" id="4"></div>
<div class="box" id="5"></div>
<div class="box" id="6"></div>
<div class="box" id="7"></div>
<div class="box" id="8"></div>
<div class="box" id="9"></div>
</div>
</body>
</html>
Step 5: Add on click event listener to boxes
<!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>
<style>
/* For Resetting any of the default margin padding in components */
* {
margin: 0px;
padding: 0px;
}
/* Utility Classes */
.game-text {
font-family: cursive, 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
font-size: 40px;
color: rgb(74, 74, 74);
font-weight: bold;
}
/* For Centering the game-box div and giving some font-family for a different look */
body {
height: 90vh;
padding: 10px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
gap: 20px;
font-family: cursive, 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
}
/* For letting the 9 boxes of tic tac toe in layout of game */
.game-box {
width: 320px;
display: grid;
gap: 10px;
grid-template-rows: 1fr 1fr 1fr;
grid-template-columns: 1fr 1fr 1fr;
}
/* Box styling and making it flex for letting any O or X in center */
.box {
width: 100px;
height: 100px;
border-radius: 18px;
background: #ffffff;
box-shadow: 10px 10px 20px #c7c7c7,
-10px -10px 20px #ffffff;
display: flex;
justify-content: center;
align-items: center;
}
</style>
</head>
<body>
<div class="game-box game-text">
<div class="box" id="1"></div>
<div class="box" id="2"></div>
<div class="box" id="3"></div>
<div class="box" id="4"></div>
<div class="box" id="5"></div>
<div class="box" id="6"></div>
<div class="box" id="7"></div>
<div class="box" id="8"></div>
<div class="box" id="9"></div>
</div>
<script>
// Add event listener to all the div having class box
function handleBoxClick(){
console.log("Box " + this.id + " was clicked.");
}
const boxes = document.querySelectorAll('.box');
boxes.forEach(box => {
box.addEventListener('click', handleBoxClick);
});
</script>
</body>
</html>
Step 6: Initialise player's turn and show O or X in game boxes on clicking and if clicked again then show alert and return
<!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>
<style>
/* For Resetting any of the default margin padding in components */
* {
margin: 0px;
padding: 0px;
}
/* Utility Classes */
.game-text {
font-family: cursive, 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
font-size: 40px;
color: rgb(74, 74, 74);
font-weight: bold;
}
/* For Centering the game-box div and giving some font-family for a different look */
body {
height: 90vh;
padding: 10px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
gap: 20px;
font-family: cursive, 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
}
/* For letting the 9 boxes of tic tac toe in layout of game */
.game-box {
width: 320px;
display: grid;
gap: 10px;
grid-template-rows: 1fr 1fr 1fr;
grid-template-columns: 1fr 1fr 1fr;
}
/* Box styling and making it flex for letting any O or X in center */
.box {
width: 100px;
height: 100px;
border-radius: 18px;
background: #ffffff;
box-shadow: 10px 10px 20px #c7c7c7,
-10px -10px 20px #ffffff;
display: flex;
justify-content: center;
align-items: center;
}
</style>
</head>
<body>
<div class="game-box game-text">
<div class="box" id="1"></div>
<div class="box" id="2"></div>
<div class="box" id="3"></div>
<div class="box" id="4"></div>
<div class="box" id="5"></div>
<div class="box" id="6"></div>
<div class="box" id="7"></div>
<div class="box" id="8"></div>
<div class="box" id="9"></div>
</div>
<script>
// Initialise playerTurn variable to know player's turn
let playerTurn = "O";
// Function to toggle player's turn
function setPlayerTurn() {
playerTurn = playerTurn === "O" ? "X" : "O";
}
// Add event listener to all the div having class box
function handleBoxClick(){
console.log("Box " + this.id + " was clicked.");
if(this.innerHTML){
// If box has something in it then the same box should not clicked more than once show alert and return
alert("Box is already filled...");
return ;
}
// Change text inside box
this.innerHTML = playerTurn;
console.log("Now player turn is changed to " + playerTurn);
setPlayerTurn();
console.log(" to " + playerTurn);
}
const boxes = document.querySelectorAll('.box');
boxes.forEach(box => {
box.addEventListener('click', handleBoxClick);
});
</script>
</body>
</html>
Step 7: Setup winning logic and play winning sound if the current player won and save the sound file named victory-sound.mp3 in same project folder
<!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>
<style>
/* For Resetting any of the default margin padding in components */
* {
margin: 0px;
padding: 0px;
}
/* Utility Classes */
.game-text {
font-family: cursive, 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
font-size: 40px;
color: rgb(74, 74, 74);
font-weight: bold;
}
/* For Centering the game-box div and giving some font-family for a different look */
body {
height: 90vh;
padding: 10px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
gap: 20px;
font-family: cursive, 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
}
/* For letting the 9 boxes of tic tac toe in layout of game */
.game-box {
width: 320px;
display: grid;
gap: 10px;
grid-template-rows: 1fr 1fr 1fr;
grid-template-columns: 1fr 1fr 1fr;
}
/* Box styling and making it flex for letting any O or X in center */
.box {
width: 100px;
height: 100px;
border-radius: 18px;
background: #ffffff;
box-shadow: 10px 10px 20px #c7c7c7,
-10px -10px 20px #ffffff;
display: flex;
justify-content: center;
align-items: center;
}
</style>
</head>
<body>
<div class="game-box game-text">
<div class="box" id="1"></div>
<div class="box" id="2"></div>
<div class="box" id="3"></div>
<div class="box" id="4"></div>
<div class="box" id="5"></div>
<div class="box" id="6"></div>
<div class="box" id="7"></div>
<div class="box" id="8"></div>
<div class="box" id="9"></div>
</div>
<script>
// Initialise playerTurn variable to know player's turn
let playerTurn = "O";
let winner = false;
let winningPattern = null;
let winningSound = new Audio("victory-sound.mp3");
const winningPatterns = [
[1, 2, 3], // Horizontal rows
[4, 5, 6],
[7, 8, 9],
[1, 4, 7], // Vertical columns
[2, 5, 8],
[3, 6, 9],
[1, 5, 9], // Diagonals
[3, 5, 7]
];
function findPatternMatch(currentPlayer, pattern) {
console.log("inside pattern" + pattern);
for (const index of pattern) {
if (document.getElementById(index.toString()).innerHTML !== currentPlayer) {
return false;
}
}
return true;
}
function checkIfCurrentPlayerWon(currentPlayer) {
for (const pattern of winningPatterns) {
let isPatternMatch = findPatternMatch(currentPlayer, pattern);
if (isPatternMatch) {
winningPattern = pattern;
winningSound.play();
return true;
}
}
return false;
}
// Function to toggle player's turn
function setPlayerTurn() {
playerTurn = playerTurn === "O" ? "X" : "O";
}
// Add event listener to all the div having class box
function handleBoxClick(){
console.log("Box " + this.id + " was clicked.");
if(this.innerHTML){
// If box has something in it then the same box should not clicked more than once show alert and return
alert("Box is already filled...");
return ;
}
// Change text inside box
this.innerHTML = playerTurn;
let currentPlayer = playerTurn;
winner = checkIfCurrentPlayerWon(currentPlayer);
console.log("Now player turn is changed to " + playerTurn);
setPlayerTurn();
console.log(" to " + playerTurn);
}
const boxes = document.querySelectorAll('.box');
boxes.forEach(box => {
box.addEventListener('click', handleBoxClick);
});
</script>
</body>
</html>
Step 8: Add some error handling if one player wins then let the users know and if they click again on boxes then say them to restart 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>
<style>
/* For Resetting any of the default margin padding in components */
* {
margin: 0px;
padding: 0px;
}
/* Utility Classes */
.game-text {
font-family: cursive, 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
font-size: 40px;
color: rgb(74, 74, 74);
font-weight: bold;
}
/* For Centering the game-box div and giving some font-family for a different look */
body {
height: 90vh;
padding: 10px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
gap: 20px;
font-family: cursive, 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
}
/* For letting the 9 boxes of tic tac toe in layout of game */
.game-box {
width: 320px;
display: grid;
gap: 10px;
grid-template-rows: 1fr 1fr 1fr;
grid-template-columns: 1fr 1fr 1fr;
}
/* Box styling and making it flex for letting any O or X in center */
.box {
width: 100px;
height: 100px;
border-radius: 18px;
background: #ffffff;
box-shadow: 10px 10px 20px #c7c7c7,
-10px -10px 20px #ffffff;
display: flex;
justify-content: center;
align-items: center;
}
</style>
</head>
<body>
<div class="player-turn-box game-text"></div>
<div class="game-box game-text">
<div class="box" id="1"></div>
<div class="box" id="2"></div>
<div class="box" id="3"></div>
<div class="box" id="4"></div>
<div class="box" id="5"></div>
<div class="box" id="6"></div>
<div class="box" id="7"></div>
<div class="box" id="8"></div>
<div class="box" id="9"></div>
</div>
<script>
// Initialise playerTurn variable to know player's turn
let playerTurn = "O";
let winner = false;
let winningPattern = null;
let winningSound = new Audio("victory-sound.mp3");
const winningPatterns = [
[1, 2, 3], // Horizontal rows
[4, 5, 6],
[7, 8, 9],
[1, 4, 7], // Vertical columns
[2, 5, 8],
[3, 6, 9],
[1, 5, 9], // Diagonals
[3, 5, 7]
];
// Function to reload game
function reloadGame() {
location.reload();
}
// Initialise the player turn box to let the user know who will be going to start
(document.getElementsByClassName("player-turn-box"))[0].innerHTML = "<h1>Player " + playerTurn + " Turn</h1>";
function findPatternMatch(currentPlayer, pattern) {
console.log("inside pattern" + pattern);
for (const index of pattern) {
if (document.getElementById(index.toString()).innerHTML !== currentPlayer) {
return false;
}
}
return true;
}
function checkIfCurrentPlayerWon(currentPlayer) {
for (const pattern of winningPatterns) {
let isPatternMatch = findPatternMatch(currentPlayer, pattern);
if (isPatternMatch) {
winningPattern = pattern;
winningSound.play();
return true;
}
}
return false;
}
// Function to toggle player's turn
function setPlayerTurn() {
playerTurn = playerTurn === "O" ? "X" : "O";
}
// Add event listener to all the div having class box
function handleBoxClick(){
// If we got our winner then say user to restart game
if (winner) {
(document.getElementsByClassName("player-turn-box"))[0].innerHTML = "<h1>Please restart game <button onclick='reloadGame()' style='border: none; background: none;'> <img width='24' height='24' src='https://img.icons8.com/material-two-tone/24/restart--v1.png' alt='restart--v1'/> </button> </h1>";
return;
}
console.log("Box " + this.id + " was clicked.");
if(this.innerHTML){
// If box has something in it then the same box should not clicked more than once show alert and return
alert("Box is already filled...");
return ;
}
// Change text inside box
this.innerHTML = playerTurn;
let currentPlayer = playerTurn;
winner = checkIfCurrentPlayerWon(currentPlayer);
console.log("Now player turn is changed to " + playerTurn);
setPlayerTurn();
console.log(" to " + playerTurn);
if (winner) {
// If we got our winner then tell the users that the current player is won
console.log("Now winner is " + playerTurn + " wohoo....");
(document.getElementsByClassName("player-turn-box"))[0].innerHTML = "<h1> ' " + currentPlayer + " ' Wons <button onclick='reloadGame()' style='border: none; background: none;'> <img width='24' height='24' src='https://img.icons8.com/material-two-tone/24/restart--v1.png' alt='restart--v1'/> </button></h1>";
}
else {
(document.getElementsByClassName("player-turn-box"))[0].innerHTML = "<h1>Player " + playerTurn + " Turn</h1>";
}
}
const boxes = document.querySelectorAll('.box');
boxes.forEach(box => {
box.addEventListener('click', handleBoxClick);
});
</script>
</body>
</html>
Animation time
Update styling in style tag for zoom in and out effect
Include this CDN Link for confetti animation
https://cdn.jsdelivr.net/npm/canvas-confetti@1.9.2/dist/confetti.browser.min.js
Make functions to animate pattern we have got already earlier stored
Final code
<!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>
<style>
/* For Resetting any of the default margin padding in components */
* {
margin: 0px;
padding: 0px;
}
/* Utility Classes */
.game-text {
font-family: cursive, 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
font-size: 40px;
color: rgb(74, 74, 74);
font-weight: bold;
}
/* For Centering the game-box div and giving some font-family for a different look */
body {
height: 90vh;
padding: 10px;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
gap: 20px;
font-family: cursive, 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
}
/* For letting the 9 boxes of tic tac toe in layout of game */
.game-box {
width: 320px;
display: grid;
gap: 10px;
grid-template-rows: 1fr 1fr 1fr;
grid-template-columns: 1fr 1fr 1fr;
}
/* Box styling and making it flex for letting any O or X in center */
.box {
width: 100px;
height: 100px;
border-radius: 18px;
background: #ffffff;
box-shadow: 10px 10px 20px #c7c7c7,
-10px -10px 20px #ffffff;
display: flex;
justify-content: center;
align-items: center;
transition: transform 0.5s ease-out;
}
.box.zoom-out {
animation: zoomOut 0.5s ease-out;
}
.box.zoom-in {
animation: zoomIn 0.5s ease-out;
}
@keyframes zoomOut {
0% {
transform: scale(1);
}
100% {
transform: scale(0);
}
}
@keyframes zoomIn {
0% {
transform: scale(0);
}
100% {
transform: scale(1);
}
}
</style>
</head>
<body>
<div class="player-turn-box game-text"></div>
<div class="game-box game-text">
<div class="box" id="1"></div>
<div class="box" id="2"></div>
<div class="box" id="3"></div>
<div class="box" id="4"></div>
<div class="box" id="5"></div>
<div class="box" id="6"></div>
<div class="box" id="7"></div>
<div class="box" id="8"></div>
<div class="box" id="9"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.9.2/dist/confetti.browser.min.js"></script>
<script>
// Initialise playerTurn variable to know player's turn
let playerTurn = "O";
let winner = false;
let winningPattern = null;
let winningSound = new Audio("victory-sound.mp3");
const winningPatterns = [
[1, 2, 3], // Horizontal rows
[4, 5, 6],
[7, 8, 9],
[1, 4, 7], // Vertical columns
[2, 5, 8],
[3, 6, 9],
[1, 5, 9], // Diagonals
[3, 5, 7]
];
// Animation of winning pattern
function animateWinningPattern(winningPattern) {
if (winningPattern) {
confetti({
particleCount: 100,
startVelocity: 50,
spread: 360,
origin: {
x: Math.random(),
// since they fall down, start a bit higher than random
y: Math.random() - 0.2
}
});
for (const index of winningPattern) {
document.getElementById(index.toString()).classList.toggle("zoom-out");
document.getElementById(index.toString()).classList.toggle("zoom-in");
}
}
}
setInterval(() => {
animateWinningPattern(winningPattern);
}, 500);
// Function to reload game
function reloadGame() {
location.reload();
}
// Initialise the player turn box to let the user know who will be going to start
(document.getElementsByClassName("player-turn-box"))[0].innerHTML = "<h1>Player " + playerTurn + " Turn</h1>";
function findPatternMatch(currentPlayer, pattern) {
console.log("inside pattern" + pattern);
for (const index of pattern) {
if (document.getElementById(index.toString()).innerHTML !== currentPlayer) {
return false;
}
}
return true;
}
function checkIfCurrentPlayerWon(currentPlayer) {
for (const pattern of winningPatterns) {
let isPatternMatch = findPatternMatch(currentPlayer, pattern);
if (isPatternMatch) {
winningPattern = pattern;
winningSound.play();
return true;
}
}
return false;
}
// Function to toggle player's turn
function setPlayerTurn() {
playerTurn = playerTurn === "O" ? "X" : "O";
}
// Add event listener to all the div having class box
function handleBoxClick(){
// If we got our winner then say user to restart game
if (winner) {
(document.getElementsByClassName("player-turn-box"))[0].innerHTML = "<h1>Please restart game <button onclick='reloadGame()' style='border: none; background: none;'> <img width='24' height='24' src='https://img.icons8.com/material-two-tone/24/restart--v1.png' alt='restart--v1'/> </button> </h1>";
return;
}
console.log("Box " + this.id + " was clicked.");
if(this.innerHTML){
// If box has something in it then the same box should not clicked more than once show alert and return
alert("Box is already filled...");
return ;
}
// Change text inside box
this.innerHTML = playerTurn;
let currentPlayer = playerTurn;
winner = checkIfCurrentPlayerWon(currentPlayer);
console.log("Now player turn is changed to " + playerTurn);
setPlayerTurn();
console.log(" to " + playerTurn);
if (winner) {
// If we got our winner then tell the users that the current player is won
console.log("Now winner is " + playerTurn + " wohoo....");
(document.getElementsByClassName("player-turn-box"))[0].innerHTML = "<h1> ' " + currentPlayer + " ' Wons <button onclick='reloadGame()' style='border: none; background: none;'> <img width='24' height='24' src='https://img.icons8.com/material-two-tone/24/restart--v1.png' alt='restart--v1'/> </button></h1>";
}
else {
(document.getElementsByClassName("player-turn-box"))[0].innerHTML = "<h1>Player " + playerTurn + " Turn</h1>";
}
}
const boxes = document.querySelectorAll('.box');
boxes.forEach(box => {
box.addEventListener('click', handleBoxClick);
});
</script>
</body>
</html>
Thank you for reading
Top comments (0)