One way to make Tic Tac Toe using Javascript

There are quite a few posts online detailing how to make Tic Tac Toe using Javascript and other languages. This isn’t meant to be a definitive “how to” on the subject, it’s more of a “how I did it”.

First I built the HTML layout and style

<div i…

There are quite a few posts online detailing how to make Tic Tac Toe using Javascript and other languages. This isn’t meant to be a definitive “how to” on the subject, it’s more of a “how I did it”.

First I built the HTML layout and style

<div id="board">
    <div class="box" data-ida="0" data-idb="0"></div>
    <div class="box" data-ida="0" data-idb="1"></div>
    <div class="box" data-ida="0" data-idb="2"></div>
    <div></div>
    <div class="box" data-ida="1" data-idb="0"></div>
    <div class="box" data-ida="1" data-idb="1"></div>
    <div class="box" data-ida="1" data-idb="2"></div>
    <div></div>
    <div class="box" data-ida="2" data-idb="0"></div>
    <div class="box" data-ida="2" data-idb="1"></div>
    <div class="box" data-ida="2" data-idb="2"></div>
    <div id="resetBtn">Reset board</div>
</div>

By itself, it’s not very impressive, just “Reset board” plonked onto the screen. It needs some style

html, body{
    font-family: arial;
}

#board{
    text-align:center;
    width: 466px;
    position: absolute;
    left: calc(50% - 338px);
    top: 35px;
}

.box{
    border: 1px solid;
    width: 150px;
    height: 150px;
    display: inline-block;
    vertical-align: top;
    cursor: pointer;
}

#resetBtn{
    margin-top: 10px;
    padding: 15px;
    background-color: green;
    width: 100px;
    position: absolute;
    left: calc(50% - 65px);
    color: #fff;
    font-weight: bold;
    cursor: pointer;
    border-radius: 15px;
    box-shadow: 3px 3px lightgrey;
    transition: .3s;
}

#resetBtn:active{
    margin-top: 12px;
    left: calc(50% - 63px);
    box-shadow: 1px 1px lightgrey;
}

Self explanatory for the board and the boxes. With the button, I added a shadow then used the :active property, which is the CSS equivalent of “onclick”, to reduce the shadow and shift the button down-right a couple of pixels to give the impression it’s being pushed. Now to add in the score board, so back to the HTML

<div id="score_board">
    <div id="sbhead">SCORE</div>
    <div class="score_block">
        <div class="sblhead">O</div>
        <div class="sblbody" data-id="o">0</div>
    </div>
    <div class="score_block">
        <div class="sblhead">X</div>
        <div class="sblbody" data-id="x">0</div>
    </div>
</div>

Again, not very impressive by itself, let’s spruce it up with some style

#score_board{
    border: 1px solid;
    width: 200px;
    height: 453px;
    top: 35px;
    margin-left: 467px;
    position: absolute;
    text-align: center;
    font-weight: bold;
}

#sbhead{
    padding: 7px 0;
    font-size: 20px;
    border-bottom: 1px solid;
}

.score_block{
    border-bottom: 1px solid;
    height: calc(50% - 20px);
}

.sblhead{
    padding: 5px;
    border: 1px solid;
    font-size: 32px;
}

.sblbody{
    font-size: 114px;
}

It might be worth noting at this point that when I’m doing the styling, I’m using the developer console built into the browser so I have a live view of the changes. For the most part I use Brave browser so I either press F12 or right click an element and click inspect, then I edit away at the styles for each element before copying them to my style sheet.

Finally I added the notifications element which will display the winning combination.

<div id="notification"></div>

and its style

#notification{
    position: absolute;
    text-align: center;
    top: 560px;
    font-size: 50px;
    margin-right: 209px;
}

Looking good so far. For the sake of aesthetics, I wrapped it all in a container and centered it on the screen. I won’t add it here, we’ll see that in the final script at the bottom. So now the template is ready, it’s time to add in the Javascript functionality.

First things first, I’ll begin with the startGame function

function startGame(){
    let player = 'O';
    document.getElementById('notification').innerHTML = '';
    var x = [['0','0','0'],['0','0','0'],['0','0','0']];
    let box = document.getElementsByClassName('box');

    var clickEvent = function() {
        let ida = this.getAttribute('data-ida');
        let idb = this.getAttribute('data-idb');
        if(this.innerText == ''){
            this.innerHTML = '<div class="check">'+player+'</div>';
            x[ida][idb] = player;
            player = (player == 'O')? 'X' : 'O';
            checkWin(x, clickEvent);
        }
    };

    for(var i = 0; i < box.length; i++){
        box[i].innerHTML = '';
        box[i].addEventListener("click", clickEvent, false);
    }

    document.getElementById('resetBtn').onclick = function(){
        startGame();
    };
}

This serves as the start of the game when the page is loaded as well as when the “reset board” button is clicked. For the sake of simplicity, in this game “O” will always be first, so we set that with

let player = 'O';

We want to make sure the notification isn’t showing anything until the game has ended so we set that element to blank

document.getElementById('notification').innerHTML = '';

Then I put the board boxes into an array with each array representing the top, the middle and the bottom set of boxes respectively

var x = [['0','0','0'],['0','0','0'],['0','0','0']];

Next I placed the box elements into a variable

let box = document.getElementsByClassName('box');

Then I created a click event variable so I could set and unset the click action of the boxes

var clickEvent = function() {
  let ida = this.getAttribute('data-ida');
  let idb = this.getAttribute('data-idb');
  if(this.innerText == ''){
   this.innerHTML = '<div class="check">'+player+'</div>';
   x[ida][idb] = player;
   player = (player == 'O')? 'X' : 'O';
   checkWin(x, clickEvent);
  }
};

Broken down: if you remember in the board, I set 2 different data attributes for each of the box elements: data-ida and data-idb. Earlier in the Javascript, I set a group of arrays which represented the board. These data attributes represent each position of the array so when we click on a box, we can change that specific position of the array and check to see if there is a win. So the first thing I did was set them to their own variables

let ida = this.getAttribute('data-ida');
let idb = this.getAttribute('data-idb');

Then, if the clicked box element is empty

if(this.innerText == ''){

we fill the box with either an ‘O’ or an ‘X’, depending on which player’s turn it is

this.innerHTML = '<div class="check">'+player+'</div>';

we then change the array we created earlier, using the position set out in the element’s data attributes that were set in the variables at the start

x[ida][idb] = player;

then we check to see which player is currently set and change it to the next player

player = (player == 'O')? 'X' : 'O';

finally, we call on the checkWin function which we will create a little later

checkWin(x, clickEvent);

Now all that’s in place, we need to iterate through the box elements to set the click event for each one

for(var i = 0; i < box.length; i++){
    box[i].innerHTML = '';
    box[i].addEventListener("click", clickEvent, false);
}

As you can see, because it serves as a fresh game, it’s clearing out all of the boxes before setting the event listener. Now when you click on the boxes, they will be filled with either an ‘O’ or an ‘X’. Should you click on ‘reset board’, they’ll be emptied again. Well, actually it’d currently throw an error because the ‘checkWin’ function hasn’t been written yet.

Finally in the startGame function, I added the click event for the reset button which basically calls upon the startGame function whenever it’s clicked

document.getElementById('resetBtn').onclick = function(){
    startGame();
}

And that’s that for the startGame function. Next we move on to the checkWin function

function checkWin(x, c){
    var a = 0;
    var b = 0;
    var winningCombinations = [
        [x[0][0] + x[1][0] + x[2][0]],
        [x[0][1] + x[1][1] + x[2][1]],
        [x[0][2] + x[1][2] + x[2][2]],
        [x[0][0] + x[0][1] + x[0][2]],
        [x[1][0] + x[1][1] + x[1][2]],
        [x[2][0] + x[2][1] + x[2][2]],
        [x[0][0] + x[1][1] + x[2][2]],
        [x[0][2] + x[1][1] + x[2][0]]
    ];
    var win;

    for(var i = 0; i < 8; i++){
        if(winningCombinations[i].includes('XXX')){
            a++;
        }
        if(winningCombinations[i].includes('OOO')){
            b++;
        }
    }

    if(a == 0 && b == 0 && (x[0].includes('0') || x[1].includes('0') || x[2].includes('0'))){
        win = "next turn";
    }
    else{
        let sblbody = document.getElementsByClassName('sblbody');
        if(a == 0 && b == 0){
            win = "draw";
        }
        else if(a > 0 && b == 0){
            win = "X wins";
            sblbody[1].innerText = parseInt(sblbody[1].innerText) + 1;
        }
        else if(a == 0 && b > 0){
            win = "O wins";
            sblbody[0].innerText = parseInt(sblbody[0].innerText) + 1;
        }
        else if(a > 0 && b > 0){
            win = "error";
        }
        gameEnd(win, c);
    }
}

It looks a lot, but it’s quite simple really when it’s broken down. First I set a couple of variables to represent each player, with a for ‘X’ and b for ‘O’

var a = 0;
var b = 0;

Then I had to work out the different winning combinations and map them to the array we created earlier in the startGame function. There are 8 possible winning combinations: 3 across, 3 down and 2 diagonal. If you imagine the arrays lined up on one another, it’s easy to determine what the possible combinations would be and place them in their own variable

var winningCombinations = [
    [x[0][0] + x[1][0] + x[2][0]],
    [x[0][1] + x[1][1] + x[2][1]],
    [x[0][2] + x[1][2] + x[2][2]],
    [x[0][0] + x[0][1] + x[0][2]],
    [x[1][0] + x[1][1] + x[1][2]],
    [x[2][0] + x[2][1] + x[2][2]],
    [x[0][0] + x[1][1] + x[2][2]],
    [x[0][2] + x[1][1] + x[2][0]]
];

Next I set an empty win variable, then iterated through the winning combinations to see whether ‘X’ or ‘O’ had a three in a row, if one or the other did, then I would add one to their respective a or b variable

var win;
for(var i = 0; i < 8; i++){
    if(winningCombinations[i].includes('XXX')){
        a++;
    }
    if(winningCombinations[i].includes('OOO')){
        b++;
    }
}

Then, if neither has scored a win, we check to see if there are any moves left on the board by checking if any of the combined arrays contain a 0. If there are moves left, the game continues

if(a == 0 && b == 0 && (x[0].includes('0') || x[1].includes('0') || x[2].includes('0'))){
    win = "next turn";
}

If there are no moves left on the board or a player has scored a win, we check through each of the possibilities

else{
    let sblbody = document.getElementsByClassName('sblbody');
    if(a == 0 && b == 0){
        win = "draw";
    }
    else if(a > 0 && b == 0){
        win = "X wins";
        sblbody[1].innerText = parseInt(sblbody[1].innerText) + 1;
    }
    else if(a == 0 && b > 0){
        win = "O wins";
        sblbody[0].innerText = parseInt(sblbody[0].innerText) + 1;
    }
    else if(a > 0 && b > 0){
        win = "error";
    }
    gameEnd(win, c);
}

Broken down: first I set a variable for the score board’s score elements

let sblbody = document.getElementsByClassName('sblbody');

then I checked to see if ‘X’ and ‘O’ had no winning combinations, in which case the win variable would be set to draw

if(a == 0 && b == 0){
    win = "draw";
}

next I checked if ‘X’ had a winning combination. If it did then the win variable would be set to “X wins” and the score element for ‘X’ would be increased by 1

else if(a > 0 && b == 0){
    win = "X wins";
    sblbody[1].innerText = parseInt(sblbody[1].innerText) + 1;
}

then I check if ‘O’ had a winning combination and repeated what I did for ‘X’

else if(a == 0 && b > 0){
    win = "O wins";
    sblbody[0].innerText = parseInt(sblbody[0].innerText) + 1;
}

then I checked if both ‘X’ and ‘O’ had winning combinations, in which case an error would be thrown

else if(a > 0 && b > 0){
    win = "error";
}

Finally I called upon the gameEnd function which we will write next

gameEnd(win, c);

And that’s all there is to the checkWin function. Let’s move on to the final function, gameEnd

function gameEnd(x, c){
    document.getElementById('notification').innerText = x;
    let box = document.getElementsByClassName('box');
    for(var i = 0; i < box.length; i++){
        box[i].removeEventListener("click", c, false);
    }
}

A pretty basic function. First we set the notification element to display the win/draw/error status

document.getElementById('notification').innerText = x;

Then we iterate through the box elements and remove the click events to prevent the game from continuing

let box = document.getElementsByClassName('box');
for(var i = 0; i < box.length; i++){
    box[i].removeEventListener("click", c, false);
}

That’s all there is to the gameEnd function. Now you can start playing. You’re going to notice, though, that the ‘X’ and ‘O’ aren’t of a proper size, so there’s one more piece of style we have to add to the game

.check{
    font-size: 130px;
    text-align: center;
}

The complete code:

HTML

<div id="container">
    <div id="board">
        <div class="box" data-ida="0" data-idb="0"></div>
        <div class="box" data-ida="0" data-idb="1"></div>
        <div class="box" data-ida="0" data-idb="2"></div>
        <div></div>
        <div class="box" data-ida="1" data-idb="0"></div>
        <div class="box" data-ida="1" data-idb="1"></div>
        <div class="box" data-ida="1" data-idb="2"></div>
        <div></div>
        <div class="box" data-ida="2" data-idb="0"></div>
        <div class="box" data-ida="2" data-idb="1"></div>
        <div class="box" data-ida="2" data-idb="2"></div>
        <div id="resetBtn">Reset board</div>
    </div>
    <div id="score_board">
        <div id="sbhead">SCORE</div>
        <div class="score_block">
         <div class="sblhead">O</div>
         <div class="sblbody" data-id="o">0</div>
        </div>
        <div class="score_block">
         <div class="sblhead">X</div>
         <div class="sblbody" data-id="x">0</div>
        </div>
    </div>

    <div id="notification"></div>
</div>

CSS

html, body{
    font-family: arial;
}

#container{
    display: flex;
    justify-content: center;
    align-items: center;
}

#board{
    text-align:center;
    width: 466px;
    position: absolute;
    left: calc(50% - 338px);
    top: 35px;
}

.box{
    border: 1px solid;
    width: 150px;
    height: 150px;
    display: inline-block;
    vertical-align: top;
    cursor: pointer;
}

.check{
    font-size: 130px;
    text-align: center;
}

#resetBtn{
    margin-top: 10px;
    padding: 15px;
    background-color: green;
    width: 100px;
    position: absolute;
    left: calc(50% - 65px);
    color: #fff;
    font-weight: bold;
    cursor: pointer;
    border-radius: 15px;
    box-shadow: 3px 3px lightgrey;
    transition: .3s;
}

#resetBtn:active{
    margin-top: 12px;
    left: calc(50% - 63px);
    box-shadow: 1px 1px lightgrey;
}

#notification{
    position: absolute;
    text-align: center;
    top: 560px;
    font-size: 50px;
    margin-right: 209px;
}

#score_board{
    border: 1px solid;
    width: 200px;
    height: 453px;
    top: 35px;
    margin-left: 467px;
    position: absolute;
    text-align: center;
    font-weight: bold;
}

#sbhead{
    padding: 7px 0;
    font-size: 20px;
    border-bottom: 1px solid;
}

.score_block{
    border-bottom: 1px solid;
    height: calc(50% - 20px);
}

.sblhead{
    padding: 5px;
    border: 1px solid;
    font-size: 32px;
}

.sblbody{
    font-size: 114px;
}

Javascript

startGame();
function startGame(){
    let player = 'O';
    document.getElementById('notification').innerHTML = '';
    var x = [['0','0','0'],['0','0','0'],['0','0','0']];
    let box = document.getElementsByClassName('box');

    var clickEvent = function() {
        let ida = this.getAttribute('data-ida');
        let idb = this.getAttribute('data-idb');
        if(this.innerText == ''){
            this.innerHTML = '<div class="check">'+player+'</div>';
            x[ida][idb] = player;
            player = (player == 'O')? 'X' : 'O';
            checkWin(x, clickEvent);
        }
    };

    for(var i = 0; i < box.length; i++){
        box[i].innerHTML = '';
        box[i].addEventListener("click", clickEvent, false);
    }

    document.getElementById('resetBtn').onclick = function(){
        startGame();
    };
}

function checkWin(x, c){
    var a = 0;
    var b = 0;
    var winningCombinations = [
        [x[0][0] + x[1][0] + x[2][0]],
        [x[0][1] + x[1][1] + x[2][1]],
        [x[0][2] + x[1][2] + x[2][2]],
        [x[0][0] + x[0][1] + x[0][2]],
        [x[1][0] + x[1][1] + x[1][2]],
        [x[2][0] + x[2][1] + x[2][2]],
        [x[0][0] + x[1][1] + x[2][2]],
        [x[0][2] + x[1][1] + x[2][0]]
    ];
    var win;

    for(var i = 0; i < 8; i++){
        if(winningCombinations[i].includes('XXX')){
            a++;
        }
        if(winningCombinations[i].includes('OOO')){
            b++;
        }
    }

    if(a == 0 && b == 0 && (x[0].includes('0') || x[1].includes('0') || x[2].includes('0'))){
        win = "next turn";
    }
    else{
        let sblbody = document.getElementsByClassName('sblbody');
        if(a == 0 && b == 0){
            win = "draw";
        }
        else if(a > 0 && b == 0){
            win = "X wins";
            sblbody[1].innerText = parseInt(sblbody[1].innerText) + 1;
        }
        else if(a == 0 && b > 0){
            win = "O wins";
            sblbody[0].innerText = parseInt(sblbody[0].innerText) + 1;
        }
        else if(a > 0 && b > 0){
            win = "error";
        }
        gameEnd(win, c);
    }
}

function gameEnd(x, c){
    document.getElementById('notification').innerText = x;
    let box = document.getElementsByClassName('box');

    for(var i = 0; i < box.length; i++){
        box[i].removeEventListener("click", c, false);
    }
}

And that’s it. A basic Tic Tac Toe game in Javascript.


Print Share Comment Cite Upload Translate
APA
ozboware | Sciencx (2024-04-18T16:59:01+00:00) » One way to make Tic Tac Toe using Javascript. Retrieved from https://www.scien.cx/2021/11/20/one-way-to-make-tic-tac-toe-using-javascript/.
MLA
" » One way to make Tic Tac Toe using Javascript." ozboware | Sciencx - Saturday November 20, 2021, https://www.scien.cx/2021/11/20/one-way-to-make-tic-tac-toe-using-javascript/
HARVARD
ozboware | Sciencx Saturday November 20, 2021 » One way to make Tic Tac Toe using Javascript., viewed 2024-04-18T16:59:01+00:00,<https://www.scien.cx/2021/11/20/one-way-to-make-tic-tac-toe-using-javascript/>
VANCOUVER
ozboware | Sciencx - » One way to make Tic Tac Toe using Javascript. [Internet]. [Accessed 2024-04-18T16:59:01+00:00]. Available from: https://www.scien.cx/2021/11/20/one-way-to-make-tic-tac-toe-using-javascript/
CHICAGO
" » One way to make Tic Tac Toe using Javascript." ozboware | Sciencx - Accessed 2024-04-18T16:59:01+00:00. https://www.scien.cx/2021/11/20/one-way-to-make-tic-tac-toe-using-javascript/
IEEE
" » One way to make Tic Tac Toe using Javascript." ozboware | Sciencx [Online]. Available: https://www.scien.cx/2021/11/20/one-way-to-make-tic-tac-toe-using-javascript/. [Accessed: 2024-04-18T16:59:01+00:00]
rf:citation
» One way to make Tic Tac Toe using Javascript | ozboware | Sciencx | https://www.scien.cx/2021/11/20/one-way-to-make-tic-tac-toe-using-javascript/ | 2024-04-18T16:59:01+00:00
https://github.com/addpipe/simple-recorderjs-demo