In this project, we will practice vanilla Javascript DOM manipulation by creating a Tic Tac Toe game.
In this step, we will create a Javascript file and connect it to our HTML file.
Note: Review the exisiting HTML so you are familiar with the structure you are interacting with
- Create a file in the
Part1
directory calledindex.js
. - Add a
script
tag at the bottom of thebody
tag and connect theindex.js
file we just created.console.log
a message to test the connection from your script file.
/index.html
<!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="./index.css" />
</head>
<body>
<nav>
<h1>Tic Tac Toe Board!</h1>
</nav>
<main>
<table>
<tr class="row">
<td id="0" onclick="play(0)"></td>
<td id="1" onclick="play(1)"></td>
<td id="2" onclick="play(2)"></td>
</tr>
<tr class="row">
<td id="3" onclick="play(3)"></td>
<td id="4" onclick="play(4)"></td>
<td id="5" onclick="play(5)"></td>
</tr>
<tr class="row">
<td id="6" onclick="play(6)"></td>
<td id="7" onclick="play(7)"></td>
<td id="8" onclick="play(8)"></td>
</tr>
</table>
<span>Player </span>
<span id="player">X</span>
<span>'s turn</span>
</main>
<script src="./index.js"></script>
</body>
</html>
In this step, we will reassign value of the player
from X
to O
when the board is clicked. This will occur after a player
has clicked the board to take their turn and it is then the next players turn.
- In index.js, create a function called
play
. We will be invoking this function any time one of 9 boxes are clicked. - Using the passed in parameter and
getElementById
select the box that was clicked and save it to a new variable. - In the
play
function, using 'getElementById', select the span in the html with the id ofplayer
. - Every time this function runs we will want to toggle the
X
to aO
or vise versa to signify the next player's turn. - At the conclusion of this step you should see that when the board is clicked, it alternates between inserting an X and an O
Detailed Instructions
- The first thing that we will want to do inside our
index.js
file is to make a function calledplay
. This function will not take in any parameters. - Inside the
play
function we need to get the element in the html document that displays who's turn it currently is. Notice that in the HTML file there is a span surrounding an X with an id ofplayer
.- To select the span we will need to use
document.getElementById('player')
and store it in a variable calledplayerSpan
so we can reference it later in the function.
- To select the span we will need to use
- Now that we have selected the span and stored it to a variable we need to toggle the text inside the html to be either
X
orO
.- To do this we need to write an if statement that checks if the
playerSpan.innerText
is===
toX
. - If
playerSpan.innerText
is equal toX
then set the the value ofplayerSpan.innerText
toO
. - Else, set the value of
playerSpan.innerText
toX
, Because if it isn'tX
It should beO
- To do this we need to write an if statement that checks if the
/index.js
function play() {
const playerSpan = document.getElementById('player');
if (playerSpan.innerText === 'X') {
playerSpan.innerText = 'O';
} else {
playerSpan.innerText = 'X';
}
}
In this step, we will put either an X
or an O
as the content of the square that is clicked.
- Open
index.js
- Add a parameter to the function
play
. The value being passed in is the id of the box that was clicked. - Using the passed in parameter and
getElementById
select the box that was clicked and save it to a variable. - Using that variable, set the
innerText
of the clicked element to have the value of the current player.
Detailed Instructions
- Inside of
index.js
we need to make some more changes to theplay
function. - Change the function so that it now takes in a parameter, it can be named what ever we want, but for clarity sake lets call it
clickedId
. - The value that gets passed in is the id of the selected element, so lets use that and
document.getElementById(clickedId)
and save it to a variable calledclickedElement
. - Inside the if statement lets set the
clickedElement.innerText
equal toX
. In the else clause, set it toO
.
/index.js
function play(clickedId) {
const playerSpan = document.getElementById('player');
const clickedElement = document.getElementById(clickedId);
if (playerSpan.innerText === 'X') {
playerSpan.innerText = 'O';
clickedElement.innerText = 'X';
} else {
playerSpan.innerText = 'X';
clickedElement.innerText = 'O';
}
}
In this step, we will create an array that will keep track of the game's progress.
- Create a variable at the top of index.js, outside the
play
function, that is assigned an empty array as its value. - In the
play
function, add the current player's value (X
orO
) to the array we defined above at the index ofclickedId
.- ex. If the top left square was clicked by player
X
, we would expectX
to be added to the array at index 0. - ex. If the very center square was clicked by player
O
, we would expectO
to be added to the array at index 4. console.log
the array in your function to keep track of it's current value (use the inspector tool by right-clicking on the page and selecting "Inspect" to view yourconsole.log
in the Console tab).
- ex. If the top left square was clicked by player
Detailed Instructions
- Create a variable at the top of the document, outside the
play
function that is equal to an empty array, lets call itboard
.- This array will be keeping track of who played what where and eventually will be how we determine if 3 items are clicked in a row.
- In the if clause of the
play
function, set the array at the index that is the same as theclickedId
to a string ofX
. We will do this by typing something like thisboard[clickedId] = 'X'
. - In the else clause of the
play
function, set the array at the index that is the same as theclickedId
to a string ofO
. We will do this by typing something like thisboard[clickedId] = 'O'
/index.js
const board = [];
function play(clickedId) {
const playerSpan = document.getElementById('player');
const clickedElement = document.getElementById(clickedId);
if (playerSpan.innerText === 'X') {
playerSpan.innerText = 'O';
clickedElement.innerText = 'X';
board[clickedId] = 'X';
} else {
playerSpan.innerText = 'X';
clickedElement.innerText = 'O';
board[clickedId] = 'O';
}
console.log(board);
}
In this step, we will complete the logic that will determine if there is a winner, and alert that winner to the window.
- You will need to determine if there is a winner, and display the winning player to the screen using
window.alert
.- There are many different ways to accomplish this task. Think through and write out each step that is needed as an outline.
- The most straightforward approach is to check every possible winning combination (this will require A LOT of if statements!)
- If there is no winner and the board is filled with all 9 spots taken up then alert that the game was a "CAT's" game.
Detailed Instructions
- We will need to be referencing each item of the array pretty often, so to make our typing a bit easier lets store each index of the array into its own variable.
- This step is not needed for the function to run properly, but it will make it easier to read.
- Lets name each square appropriately by naming each of the 9 variables based off its location in the grid. For instance
topRight
,topCenter
, andmiddleCenter
.
- We next need to check if a winner has been determined. To do that we need to check every possible winning combination. For instance if
topLeft
,topMiddle
andtopRight
all equal each other then we know we have a winner, almost. It is possible that all 3 squares have no value so they would all equalundefiend
, which is not a winner. Because of that we need to first check that one of the squares does not equalundefined
.- It will look something like this
if (topRight !== undefined && topRight === topCenter && topRight === topRight)
- In the case that all 3 are the same values then we will alert the winner. We can tell who won by looking at any of the 3 values, as what those variables hold is who played in those squares.
- It will look something like this
- Finally we need to check if the board has been filled in yet. To do this we are going to write a for loop that loops exactly 9 times, and check each index of the array.
- If any of the index of the array contains
undefined
, we know that the array is not yet full. To keep track of this we need to create a variable calledboardFull
that by default is set to true. If we ever see the valueundefined
then we need to set its value tofalse
. - After the for loop if the value of
boardFull
is is still true we need to alert, that it was a cats game.
- If any of the index of the array contains
Treat this as a toy problem. There are many solutions to finding a winner in Tic-Tac-Toe, so try your best to solve this on your own before looking at the solution below.
/index.js
const board = [];
function play(clickedId) {
const playerSpan = document.getElementById('player');
const clickedElement = document.getElementById(clickedId);
if (playerSpan.innerText === 'X') {
playerSpan.innerText = 'O';
clickedElement.innerText = 'X';
board[clickedId] = 'X';
} else {
playerSpan.innerText = 'X';
clickedElement.innerText = 'O';
board[clickedId] = 'O';
}
console.log(board);
const topLeft = board[0];
const topCenter = board[1];
const topRight = board[2];
const middleLeft = board[3];
const middleCenter = board[4];
const middleRight = board[5];
const bottomLeft = board[6];
const bottomCenter = board[7];
const bottomRight = board[8];
// CHECKS ALL WINNING COMBINATIONS
if (topLeft !== undefined && topLeft === topCenter && topLeft === topRight) {
alert(`${topLeft} is the winner`);
return;
}
if (middleLeft !== undefined && middleLeft === middleCenter && middleLeft === middleRight) {
alert(`${middleLeft} is the winner`);
return;
}
if (bottomLeft !== undefined && bottomLeft === bottomCenter && bottomLeft === bottomRight) {
alert(`${bottomLeft} is the winner`);
return;
}
if (topLeft !== undefined && topLeft === middleLeft && topLeft === bottomLeft) {
alert(`${topLeft} is the winner`);
return;
}
if (topCenter !== undefined && topCenter === middleCenter && topCenter === bottomCenter) {
alert(`${topCenter} is the winner`);
return;
}
if (topRight !== undefined && topRight === middleRight && topRight === bottomRight) {
alert(`${topRight} is the winner`);
return;
}
if (topLeft !== undefined && topLeft === middleCenter && topLeft === bottomRight) {
alert(`${topLeft} is the winner`);
return;
}
if (bottomLeft !== undefined && bottomLeft === middleCenter && bottomLeft === topRight) {
alert(`${bottomLeft} is the winner`);
return;
}
// DETERMINES IF THE BOARD IS FULL, ALERTS WHEN IT IS
let boardFull = true;
for (let i = 0; i <= 8; i++) {
if (board[i] === undefined) {
boardFull = false;
}
}
if (boardFull === true) {
alert("Cat's game, there is no winner");
}
}
- Create a function that resets the board after each game. This function should be called after the winner is revealed.
- Add a button that runs the reset function when it is clicked.
- The current logic allows players to override a square that has already been played. Alter the function to remove the ability to override a square.
- Alter the game so that users can not continue to play after a winner has been decided.
If you see a problem or a typo, please fork, make the necessary changes, and create a pull request so we can review your changes and merge them into the master repo and branch.
© DevMountain LLC, 2019. Unauthorized use and/or duplication of this material without express and written permission from DevMountain, LLC is strictly prohibited. Excerpts and links may be used, provided that full and clear credit is given to DevMountain with appropriate and specific direction to the original content.