Sorry if it's a dumb question, currently making a tic tac toe game, I made the user able to click the board which are buttons. When clicked upon, it will place the players marker("X" or "O").
Problem: Once a player has pressed it, I do not want that textContent to change at all. Is there a way to achieve that result?
const playRound = (e) => {
let playerInput = e.currentTarget.dataset.number
placeMarker(playerInput, getActivePlayer().marker);
e.currentTarget.textContent = getActivePlayer().marker;
_switchPlayerTurn();
printNewRound();
console.log(`${board}`);
console.log(typeof board);
console.log(GameBoard.getBoard());
}
//Event Listener
const ScreenController = (function ScreenController() {
const {
playRound
} = Game;
const board = document.querySelectorAll('.square');
board.forEach(element => {
element.addEventListener('click', playRound);
})
})();
Codepen:https://codepen.io/jimmyjimenez2400/pen/ZEjvrPq
I did try to look up of how I can make it stay permanent with no luck.
I did try to use innerHTML but knowing it can cause issues for security. I just avoid it.
Feeling that I probably have to make an if statement, where "If textContent changed, then somehow lock it". I'm expecting the textContent to not change on second click on the element.
CodePudding user response:
I think setting the once
option to true
is the most concise:
element.addEventListener('click', playRound, { once: true });
Although it is not supported by IE: https://caniuse.com/once-event-listener
docs: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#parameters
Example:
let count = 0;
const playRound = (e) => {
const letter = count % 2 == 0 ? "O" : "X";
e.target.textContent = letter;
console.log(`Turn ${count} complete`);
}
const board = document.querySelectorAll('.square');
board.forEach(element => {
element.addEventListener('click', playRound, { once: true });
})
.row {
display: flex
}
.square {
display: flex;
cursor: pointer;
width: 40px;
height: 40px;
border: 1px solid black;
align-items: center;
justify-content: center;
}
div.as-console-wrapper {
max-height: 66px;
}
<div >
<span ></span>
<span ></span>
<span ></span>
</div>
<div >
<span ></span>
<span ></span>
<span ></span>
</div>
<div >
<span ></span>
<span ></span>
<span ></span>
</div>
As suggested in the comments, here is a slightly less concise method that supports IE. It does the exact same thing as above, removes the event listener at the end of the callback function.
let count = 0;
const playRound = (e) => {
const letter = count % 2 == 0 ? "O" : "X";
e.target.textContent = letter;
console.log(`Turn ${count} complete`);
}
const board = document.querySelectorAll('.square');
board.forEach(element => {
const callback = (e) => {
playRound(e);
element.removeEventListener('click', callback);
}
element.addEventListener('click', callback);
})
.row {
display: flex
}
.square {
display: flex;
cursor: pointer;
width: 40px;
height: 40px;
border: 1px solid black;
align-items: center;
justify-content: center;
}
div.as-console-wrapper {
max-height: 66px;
}
<div >
<span ></span>
<span ></span>
<span ></span>
</div>
<div >
<span ></span>
<span ></span>
<span ></span>
</div>
<div >
<span ></span>
<span ></span>
<span ></span>
</div>
CodePudding user response:
Simply add an if clause right at the beginning of your event listener function, like
const playRound = (e) => {
if (e.currentTarget.textContent) return;
...
}
This will effectively "lock" any already filled out cell. An empty cell will have a .textContent
of ""
which in JavaScript will be seen as falsy.
Ok, if the initial content is numeric, then you should of course check differently, like:
if ("XO".includes(e.currentTarget.textContent)) return;