<body>
<div id="wrapper">
</div>
<script>
const $wrapper = document.querySelector('#wrapper');
const $btn = document.querySelector('#btn');
const total = 12;
const colors = ['url(/img/1.jpeg) no-repeat center / cover', 'url(/img/2.jpeg) no-repeat center / cover', 'url(/img/3.jpeg) no-repeat center / cover', 'url(/img/4.jpeg) no-repeat center / cover', 'url(/img/5.jpeg) no-repeat center / cover', 'url(/img/6.jpeg) no-repeat center / cover'];
let colorSlice = colors.slice(0, total / 2);
let colorCopy = colorSlice.concat(colorSlice);
let shuffled = [];
let completed = [];
let clicked = [];
let clickable = false;
let startTime;
function shuffle() { //피셔-예이츠 셔플
for (let i = 0; colorCopy.length > 0; i += 1) {
const randomIndex = Math.floor(Math.random() * colorCopy.length);
shuffled = shuffled.concat(colorCopy.splice(randomIndex, 1));
}
}
function createCard(i) { //div.card > div.card-inner > (div.card-front + div.card-back)
const card = document.createElement('div');
card.className = 'card'; //card태그 생성
const cardInner = document.createElement('div');
cardInner.className = 'card-inner'; //card-inner 태그 생성
const cardFront = document.createElement('div');
cardFront.className = 'card-front'; // card-front 태그 생성
const cardBack = document.createElement('div');
cardBack.className = 'card-back'; //card-back 태그 생성
cardBack.style.background = shuffled[i];
cardInner.appendChild(cardFront);
cardInner.appendChild(cardBack);
card.appendChild(cardInner);
return card;
}
function onClickCard() {
if (!clickable || completed.includes(this) || clicked[0] === this) {
return;
}
this.classList.toggle('flipped');
clicked.push(this);
if (clicked.length !== 2) {
return;
}
const firstBackColor = clicked[0].querySelector('.card-back').style.background;
const secondBackColor = clicked[1].querySelector('.card-back').style.background;
if (firstBackColor === secondBackColor) { //두 카드가 같은 카드면
completed.push(clicked[0]);
completed.push(clicked[1]);
clicked = [];
if (completed.length !== total) {
return;
}
const endTime = new Date();
setTimeout(() => {
alert(`축하합니다! ${(endTime - startTime) / 1000}초 걸렸습니다.`);
resetGame();
}, 1000);
return;
}
clickable = false; //4번클릭하면 2개 남는 버그 수정
setTimeout(() => {
clicked[0].classList.remove('flipped');
clicked[1].classList.remove('flipped');
clicked = [];
clickable = true;
}, 500);
}
function startGame() {
clickable = false;
shuffle();
for (let i = 0; i < total; i += 1) {
const card = createCard(i);
card.addEventListener('click', onClickCard);
$wrapper.appendChild(card);
}
document.querySelectorAll('.card').forEach((card, index) => { //초반 카드 공개
setTimeout(() => {
card.classList.add('flipped');
}, 1000 + 100 * index);
});
setTimeout(() => { //카드감추기
document.querySelectorAll('.card').forEach((card) => {
card.classList.remove('flipped');
});
clickable = true;
startTime = new Date();
}, 5000);
}
startGame();
function resetGame() {
$wrapper.innerHTML = '';
colorCopy = colors.concat(colors);
shuffled = [];
completed = [];
startGame();
}
</script>
</body>
JavaScript의 런타임 모델은 코드의 실행, 이벤트의 수집과 처리, 큐에 대기 중인 하위 작업을 처리하는 이벤트 루프에 기반
위 예제를 실행하며 카드를 연속 4번 클릭했을때 두장의 카드가 같지 않음에도 불구하고 앞면인 상태로 유지되는 버그가 발생하였었다.
이는 이벤트루프때문에 발생한 버그로 기존코드를 아래와 같이 수정하였다.
기존코드
function onClickCard() {
if (!clickable || completed.includes(this) || clicked[0] === this) {
return;
}
this.classList.toggle('flipped');
clicked.push(this);
if (clicked.length !== 2) {
return;
}
const firstBackColor = clicked[0].querySelector('.card-back').style.background;
const secondBackColor = clicked[1].querySelector('.card-back').style.background;
if (firstBackColor === secondBackColor) { //두 카드가 같은 카드면
completed.push(clicked[0]);
completed.push(clicked[1]);
clicked = [];
if (completed.length !== total) {
return;
}
const endTime = new Date();
setTimeout(() => {
alert(`축하합니다! ${(endTime - startTime) / 1000}초 걸렸습니다.`);
resetGame();
}, 1000);
return;
}
수정된 코드
function onClickCard() {
if (!clickable || completed.includes(this) || clicked[0] === this) {
return;
}
this.classList.toggle('flipped');
clicked.push(this);
if (clicked.length !== 2) {
return;
}
const firstBackColor = clicked[0].querySelector('.card-back').style.background;
const secondBackColor = clicked[1].querySelector('.card-back').style.background;
if (firstBackColor === secondBackColor) { //두 카드가 같은 카드면
completed.push(clicked[0]);
completed.push(clicked[1]);
clicked = [];
if (completed.length !== total) {
return;
}
const endTime = new Date();
setTimeout(() => {
alert(`축하합니다! ${(endTime - startTime) / 1000}초 걸렸습니다.`);
resetGame();
}, 1000);
return;
}
clickable = false; //4번클릭하면 2개 남는 버그 수정
setTimeout(() => {
clicked[0].classList.remove('flipped');
clicked[1].classList.remove('flipped');
clicked = [];
clickable = true;
}, 500);
}
clickable = false; 를 넣어줌으로써 2번 클릭한 후에는 다른 카드를 클릭할 수 없게 막아준다.
이벤트 루프를 참고하여 버그를 해결할 수 있다.