I am practicing html/vanilla JS by building a tic tac toe app. I add an event listener to the board square <div>
s, and create elements with X and O and insert them into the DOM, then call a function to check if there is a winner. The checkIfPlayerWins()
function does some checking and then calls and alert
if the players wins or there is a draw.
The problem I am running into is that if a player wins, the alert is being called before the X or O is inserted into the DOM which just looks funny. I would like to not use a setTimeout
as that just seems a bit hacky. I tried async
/await
and also creating a new Promise
from it but can't seem to get it to work.
Okay here is some code and relevant links: insertAdjacentElement MDN docs My github/ code repo
setEventListeners() {
const board = document.querySelector('.board')
const squares = board.children
for (let i = 0; i < squares.length; i++) {
squares[i].addEventListener('click', (event) => {
const squareEl = event.target
if (!squareEl.firstChild) {
this.makeMove(squareEl)
} else {
alert('That square is already taken!')
}
})
}
}
makeMove(eventTarget) {
let playerMarker = ''
if (this.playerOneTurn) {
playerMarker = 'X'
} else {
playerMarker = 'O'
}
eventTarget.insertAdjacentElement('beforeend', createElementFromHTML(`<div>${playerMarker}</div>`))
this.updateGameState(eventTarget.className, playerMarker)
this.playerOneTurn = !this.playerOneTurn
this.updatePlayerTurnText()
}
export function createElementFromHTML(htmlString) {
const template = document.createElement('template')
template.innerHTML = htmlString.trim()
return template.content.firstChild
}
updateGameState(squareName, playerMarker) {
this.gameState[squareName] = playerMarker
this.checkIfPlayerWins()
}
checkIfPlayerWins() {
// TODO make this smarter
const winningIndexes = [
[0, 1, 2],
[3, 4, 5],
[6, 7, 8],
[0, 3, 6],
[1, 4, 7],
[2, 5, 8],
[0, 4, 8],
[2, 4, 6],
]
let winner = null
winningIndexes.forEach((combos) => {
if (combos.every((combo) => this.gameState[`square-${combo + 1}`] === 'X')) {
winner = this.playerOne
} else if (combos.every((combo) => this.gameState[`square-${combo + 1}`] === 'O')) {
winner = this.playerTwo
}
})
if (Object.values(this.gameState).every((square) => square != '')) {
alert('Game is a draw!')
} else if (winner) {
alert(`${winner} wins!!`)
}
}
These are all the relevant functions, and if you see in the makeMove()
function, I call eventTarget.insertAdjacentElement
, but then I call updatePlayerTurn()
which calls checkIfPlayerWins()
which calls the alert()
. This alert()
in the checkIfPlayerWins()
function is executing before the insertAdjacentElement
element gets inserted into the DOM. Can anyone help me figure out how I can wait for the DOM changes to happen before the alert()
gets called? Thanks!