Assignment - DOM Tic Tac Toe Assignment

A place to post your questions or solutions

@zsolt-nagy @thecil
I completed my DOM Tic Tac Toe Assignment. I used Flexbox to center all the larger elements in the page. I used Grid to create the board. Then I used Flexbox inside each tile to center the X’s and O’s. I spent some time on the Internet learning how Grid and Flexbox work together. Pretty cool.

DOM/JS question:
I could only get my javascript to run if I placed

<script src="./tictactoe.js"></script>

at the bottom of the HTML file. I don’t understand why this is the case because I check if DOMContentLoaded at the beginning of the JS file, which I learned from MDN Web Docs.

I also tried adding the defer attribute to the script element and that did not work, so this behavior is a mystery to me.

Can you please explain? Thanks much.

Here is the link to my solution on GitHub: https://github.com/ren132710/tic-tac-toe

And here is the JS code:

/* Ensure the page has fully loaded before setting event listeners */
if (document.readyState == 'loading') {
    document.addEventListener('DOMContentLoaded', ready)
} else {
    ready();
}

const tiles = Array.from(document.querySelectorAll('.tile'));
const displayTurn = document.querySelector('.display-turn');
const resetButton = document.querySelector('.btn-reset');
const announcer = document.querySelector('.announcer');

let board = ['','','','','','','','',''];
let currentPlayer = 'X';
let isGameActive = true;

const PLAYERX_WON = 'PLAYERX_WON';
const PLAYERO_WON = 'PLAYERO_WON';
const DRAW = 'DRAW';
    
/*
    Indexes within the board
    [0] [1] [2]
    [3] [4] [5]
    [6] [7] [8]
*/

const winningCombos = [
    [0, 1, 2],
    [3, 4, 5],
    [6, 7, 8],
    [0, 3, 6],
    [1, 4, 7],
    [2, 5, 8],
    [0, 4, 8],
    [2, 4, 6]
];

//create the event listeners
function ready() {

    tiles.forEach( (tile, index) => {
        tile.addEventListener('click', () => getMove(tile, index));
    });

    resetButton.addEventListener('click', resetBoard);
}

function getMove(tile, index) {
    if(isMoveValid(index) && isGameActive) {
        tile.innerText = currentPlayer;
        tile.classList.add(`player${currentPlayer}`);
        updateBoard(index);
        //unless the game is over
        if ( !isGameOver() ) {
            currentPlayer = changePlayer(currentPlayer);
        }
    }
}

function isMoveValid (index) {
    let result = board[index] === '' ? true : false;
    return result;
   }

function updateBoard(index) {
    board[index] = currentPlayer;
}

function isGameOver() {
    let gameWon = false;

    //check if there is a winner
    for (let i = 0; i <= 7; i += 1) {
        const winCondition = winningCombos[i];
        const a = board[winCondition[0]];
        const b = board[winCondition[1]];
        const c = board[winCondition[2]];
        if (a === '' || b === '' || c === '') {
            continue;
        }
        if (a === b && b === c) {
            gameWon = true;
            break;
        }
    }
    
    if (gameWon) {
        announce(currentPlayer === 'X' ? PLAYERX_WON : PLAYERO_WON);
        isGameActive = false;
        return true;
    }

    if ( !board.includes('') ) { 
        announce(DRAW);
        isGameActive = false;
    }
  return false;
}

function changePlayer(currentPlayer) {
    displayTurn.classList.remove(`player${currentPlayer}`);
    result = currentPlayer === 'X' ? 'O' : 'X';
    displayTurn.innerText = result;
    displayTurn.classList.add(`player${result}`);
    return result;
}

function announce(message) {
    switch(message){
        case PLAYERO_WON:
            announcer.innerHTML = 'Player <span class="playerO">O</span> won!! Game over.';
            break;
        case PLAYERX_WON:
            announcer.innerHTML = 'Player <span class="playerX">X</span> won!! Game over.';
            break;
        case DRAW:
            announcer.innerHTML = 'It\'s a draw. Game over.';
            break;
    }
    announcer.classList.remove('hide');
}

function resetBoard () {
    board = ['', '', '', '', '', '', '', '', ''];
    isGameActive = true;
    announcer.classList.add('hide');

    if (currentPlayer === 'O') {
        currentPlayer = changePlayer(currentPlayer);
    }

    tiles.forEach(tile => {
        tile.innerText = '';
        tile.classList.remove('playerX');
        tile.classList.remove('playerO');
    });
}
2 Likes

We only learned placing the JavaScript files at the bottom of the body tag to avoid these problems.

Technically, this is sufficient for you to make progress until you become advanced in JavaScript, because then, you won’t include JavaScript files in script tags, but you will use Webpack and npm to generate a JavaScript bundle, and you will simply use a template html file with a predefined script tag. This is out of scope for this course though.

2 Likes

Hi @zsolt-nagy
Please help me here. The background color and border is not reflecting.
dom.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Static Template</title>
    <link rel="stylesheet" href="./css/style.css" />
  </head>
  <body>
    <h1>DOM Manipulation</h1>
    <h2>Class lists</h2>
    <div class="js-node">Text</div>
    <h2>Attributes</h2>
    <a href="#" class="js-moralis-link">Visit moralis</a>
    <h2>Data-attributes</h2>
    <script src="./script.js"></script>
  </body>
</html>

style.css

.bg-grey {
    background-color: gray;
}
.border-darkred {
    border: 4px dashed darkred;
}

script.js

const $node = document.querySelector(".js-node");
const $link = document.querySelector(".js-moralis-link");
$node.classList.add(".bg-grey");
$node.classList.add(".border-darkred");
$link.setAttribute("href", "https://moralis.io/");

1 Like

most likely the address of one of your files (css or js) were not resolved properly. Open a codesandbox and paste your code in there. If it is still not working, post it here and someone will be able to easily point out the problem.

2 Likes