지뢰 찾기를 만들자 (HTML, CSS, JS)

1K3H·2020년 11월 21일
1

JavaScript

목록 보기
1/2
post-thumbnail
post-custom-banner

github - 전체 코드



💡 발단


  • 온라인으로 JS에 관한 튜토리얼 강의들을 들으며 깨달은 점은 튜토리얼의 내용을 따라서 코드를 완성하고 나면 튜토리얼을 완주한 것에서 오는 성취감은 크지만 튜토리얼에 나오는 내용을 내 것으로 체득? 했다고 말하기는 쉽지 않다는 것이었다. 그래서 지금까지 배운 문법을 토대로 나혼자 뭐든 만들어보자는 생각을 갖고 있었다.

  • 어떤 것을 구현해볼까 찾아보던 중 우연히 지뢰찾기를 발견했고 평소에는 별로 관심이 없던 지뢰찾기에 갑자기 빠져서 한동안 지뢰찾기를 플레이 하다가 '이정도는 내가 구현할 수 있지 않을까?'하는 생각에 지뢰찾기를 만들기로 했다




🔎 '지뢰 찾기' 살펴보기


  • 베이스는 컴퓨터에 있는 'Microsoft Minesweeper'의 '주문제작' 부분으로 했다.

  • 게임방법


먼저 보드의 크기와 보드에 들어갈 지뢰의 개수를 설정한다. 그리고 게임 시작 버튼을 누르면

이렇게 보드가 생성된다.

각 타일들을 클릭하면 지뢰의 유무나 주변 타일의 지뢰 유무에 따라 타일에 표시되는 정보가 다른데 정리해보면

주변 타일: 클릭한 타일을 기준으로 상, 하, 좌, 우 한 칸씩 그리고 좌상, 우상, 좌하, 우하 대각으로 한 칸씩

마우스 좌 클릭

  • 지뢰 타일: 선택시 게임종료
  • 숫자 정보가 담긴 타일: 주변 타일에 지뢰가 몇개 있는지 표시
  • 하얀색 타일: 주변 타일에 지뢰가 전혀 없다는 의미. 이 타일을 선택하면 주변 타일 중 숫자 정보가 담긴 타일은 함께 표시하고 만약 주변 타일에 자신과 같은 하얀 타일이 있다면 이 타일도 동시에 같은 작업을 수행함

마우스 우 클릭

  • 깃발 타일: 숫자 타일을 토대로 지뢰 타일이라고 확신할 수 있는 타일을 사용자가 설정할 수 있다(우 클릭 1회)
  • 물음표 타일: 지뢰 타일인지 아닌지 확신할 수 없을 때 사용(깃발 타일 상태에서 우 클릭시 사용가능)

마우스 좌우 동시 클릭 or 마우스 휠 클릭

숫자 타일을 마우스로 좌우 동시 클릭 하거나 휠 클릭 하면 주변 타일들 중 비활성화된 타일들(어떤 정보도 갖고 있지 않은 타일들)을 표시한다. 이 과정에서 숫자 타일의 주변 타일에 숫자 타일의 숫자 만큼의 깃발 타일이 존재한다면 비활성화되어 있는 타일들은 자동으로 작동하게 된다.

숫자 '2' 타일 선택 시 비활성 중인 타일 어둡게 표시 > 깃발 타일의 개수가 2개 이므로 비활성 타일 자동으로 작동




💻 기능 구현하기



1. 입력값을 받아서 보드 만들기

우선 일정한 크기의 타일을 갖는 보드를 생성하기 위해서 어떻게 해야될지 고민을 하다가 html의 table을 이용하면 될 것 같다는 생각을 했다.

html파일에 input태그를 만들고 가로 길이와 세로 길이의 입력값을 받아서 table을 만들면 될 것 같다고 생각은 했는데 그 과정을 코드로 어떻게 짜야 되는지 몰라서 검색을 해봤다.

티스토리 Wallel - JavaScript 동적테이블 생성(행, 열 입력받아서)

index.html

<body>
    <div id="gameBoard">
        <input type="number" id="row" placeholder="가로">
        <input type="number" id="col" placeholder="세로">
        <button id="startBtn">게임 시작</button>
    </div>
    <script src="program.js"></script>
</body>

program.js

const startBtn = document.getElementById("startBtn");
startBtn.addEventListener("click", setGame);

function setGame() {
  const row = parseInt(document.getElementById("row").value);
  const col = parseInt(document.getElementById("col").value);
    
  makeBoard(row, col);
    
  function makeBoard(rowNum, colNum) {
    let tableEle = '<table>';

    for (let i = 0; i < colNum; i++) {
      tableEle += '<tr>';
      for (let j = 0; j < rowNum; j++) {
        tableEle += '<td></td>'
      }
      tableEle += '</tr>';
    }
    tableEle += '</table>';
    document.getElementById("gameBoard").innerHTML = tableEle;
  }  
}


✔ parseInt()

MDN - parseInt()

documet.getElementById().value를 사용해 input태그의 입력값을 가져오면 typeof로 확인 시 string 타입인 것을 확인할 수 있다. 입력값을 다른 식에서 활용하기 위해선 값을 number 타입으로 바꿔줘야 되는데 그럴 때 parseInt()를 이용해서 변환이 가능하다.

✔ 중첩 반복문

table 태그에서는 tr 태그로 묶인 td 태그들이 한 개의 열을 구성한다. 위의 코드를 풀어보면 처음 for 반복문에서 tr 태그로 처음 열을 시작하고 다음 for 반복문을 통해 행을 추가한 다음 반복이 끝나면 처음의 반복문으로 돌아가 /tr 태그로 시작했던 열을 닫고 다음 열을 시작하는 과정을 반복한다.

(반복문 사용이 익숙하지 않아서 초깃값 설정과 반복 조건을 통해서 얼만큼 반복을 해야되는지 설정하는 부분에서 애를 좀 먹었다.)

✔ innerHTML

MDN - Element.innerHTML

innerHTML 의 값을 설정(대입)하면 요소의 모든 자손이 제거되고, 문자열 htmlString에 지정된 HTML을 파싱하고, 생성된 노드로 대체합니다.




2. 입력값을 범위로 하는 중복 없는 난수 만들기(지뢰 위치 설정)

게임 이름이 '지뢰 찾기'인 만큼 게임 기능 구현에 있어서 보드의 어느 위치에 지뢰를 위치하게 할 것인지 설정하는 것이 중요한 요소인 것 같았다. 각각의 타일에 '지뢰'라는 개념을 어떻게 넣을 것인지 모르는 상태에서 우선 지뢰에 해당하는 위치를 나타내는 숫자들의 그룹을 만들어야 겠다는 생각을 했다.

사용해 봤던 Math.random()과 Math.floor()를 사용해서 번호를 뽑아봤는데 그렇게 나온 결과를 보고 있으니 중복되는 값이 존재했다. 중복값이 존재하면 입력값으로 받은 범위에 못 미치는 지뢰개수를 갖게 된다.

중복값 없이 Math.random()과 Math.floor()의 결괏값을 얻을 수 없을까 검색!

티스토리 Code Playground - 중복값 없는 랜덤 숫자 추출하는 여러가지 방법

index.html

<body>
  <div id="gameBoard">
    ...
    <input type="number" id="mineNum" placeholder="지뢰 개수">
    ...
  </div>
  ...
</body>

program.js

const startBtn = document.getElementById("startBtn");
startBtn.addEventListener("click", setGame);

function setGame() {
  const row = parseInt(document.getElementById("row").value);
  const col = parseInt(document.getElementById("col").value);
  const mineNum = parseInt(document.getElementById("mineNum").value);
    
  makeBoard(row, col);
  setMineNumArr(mineNum, row * col);
}

function makeBoard(rowNum, colNum) {...}  

function setMineNumArr(numLimit, numRange) {
  mines = [];
  for (let i = 0; i < numLimit; i++) {
    let randomNum = Math.floor(Math.random() * numRange);
    if (!findMine(randomNum)) {
      mines.push(randomNum);
    } else {
      i--;
    }
  }
  function findMine(j) {
    return mines.find((e) => (e === j));
  }
}

✔ 화살표 함수

MDN - Arrow function(화살표 함수)

//기존 함수 표현식
function(e) { return e === j };

//화살표 함수 표현식
(e) => { return e === j };
//인수가 하나만 있으면 인수를 묶는 괄호를 생략할 수 있다.
e => { return e === j };
//함수 몸통 안의 문장이 return뿐이면 중괄호와 return 키워드 생략가능
//but 반환값이 객체 리터럴이면 ()로 묶어야함
e => e === j;

//출처: 도서 '모던 자바스크립트 입문'  

✔ 함수의 결괏값을 조건문의 조건으로 사용하기

✔ Array.push(), Array.find()

MDN - Array.prototype.push()
MDN - Array.prototype.find()

Array 메서드 다시 한 번 훑어보기

🔨 mines에 선언자 없이 전역 변수로 사용한 부분 수정

function setGame() {
  const row = parseInt(document.getElementById("row").value);
  const col = parseInt(document.getElementById("col").value);
  const mineNum = parseInt(document.getElementById("mineNum").value);
  const mineArr = setMineNumArr(mineNum, row * col);
    
  makeBoard(row, col);
  //setMineNumArr(mineNum, row * col);
}

function setMineNumArr(numLimit, numRange) {
  let mineArr = [];
  for (let i = 0; i < numLimit; i++) {
    let randomNum = Math.floor(Math.random() * numRange);
    if (!findMine(randomNum)) {
      mineArr.push(randomNum);
    } else {
      i--;
    }
  }
  function findMine(j) {
    return mineArr.find((e) => (e === j));
  }
  return mineArr;
}

지뢰의 위치가 담긴 배열을 만들고 다른 함수에서 그 배열을 이용하기 위해 고민하다가 변수 선언 시 선언자를 쓰지 않으면 전역 변수로 사용 가능하다는 것을 알고 선언자 없이 사용했지만 이런 방법으로 변수를 선언하면 변수의 충돌을 일으킬 수 있어서 좋은 방법이 아니라는 걸 배우고 함수에서 완성된 배열을 return으로 반환하여 그 값을 새로운 변수로 저장하게끔 바꿈




3. 보드에 지뢰 세팅하기

지뢰가 위치할 번호를 뽑아서 배열에 담아뒀으니 이제 이 배열을 이용해서 만들어진 보드에 지뢰를 담는 과정을 진행해야 했다. mines 배열을 만들면서 사용된 배열을 돌며 해당 요소가 배열에 있는지 확인하는 작업을 통해서 보드에도 같은 작업을 하면 될 것 같다는 생각을 했는데 문제는 보드를 만드는 함수를 통해 만들어진 table 안의 요소에 접근해서 그 요소들을 배열에 담는 방법이 생각보다 까다롭다는 점이었다.

js에서 html요소에 접근하는 방법으로 알고 있던 것들은 각 요소의 id나 class를 getElementById() 또는 getElementByClass()로 불러오는 법 뿐이었는데 그러려면 보드를 만드는 반복문 내용중 td태그를 삽입하는 과정에 id나 class를 추가하는 작업을 진행해야 했다.

그렇게 td 요소에 선택자를 추가하는 방법으로 작업을 진행하던 와중에 vscode에서 getElementBy를 치면 자동완성으로 생성되는 목록에 getElementByTagName이 있는 걸 발견하고 이름만 봤을 때 'html의 요소를 태그 별로 꺼낼 수 있는 건가?' 싶어서 검색을 해봤다.

✔ getElementByTagName(), HTMLCollection

MDN - Document.getElementsByTagName()
설명을 읽어보니 html파일에서 tag를 한 번에 불러와서 무려 배열 형태의 객체인 HTMLCollection 쓸 수 있다고 한다.(HTMLcollection은 배열의 모습을 하고 있지만 배열 메서드를 사용할 수 없다.)

getElementByTagName('td')를 통해 생성된 td요소를 모두 불러와서 변수에 저장하고 이제 이 요소를 반복문을 통해 mines배열과 내부 요소를 비교하며 일치하는 위치에 mine이라는 class를 추가하면 원하는 결과를 얻을 수 있을 것 같다.

✔ Element.classList

티스토리 Dev Life in IT - Javascript class 명 추가/삭제
MDN - Element.classList

program.js

...
const tdArr = document.getElementsByTagName('td');


function setGame() {
  const row = parseInt(document.getElementById("row").value);
  const col = parseInt(document.getElementById("col").value);
  const mineNum = parseInt(document.getElementById("mineNum").value);
  const mineArr = setMineNumArr(mineNum, row * col);
    
  makeBoard(row, col);
    
  putMineInBoard(mineArr);
}

function makeBoard(rowNum, colNum) {...}  

function setMineNumArr(numLimit, numRange) {
  let mineArr = [];
  for (let i = 0; i < numLimit; i++) {
    let randomNum = Math.floor(Math.random() * numRange);
    if (!findMine(randomNum)) {
      mineArr.push(randomNum);
    } else {
      i--;
    }
  }
  function findMine(j) {
    return mineArr.find((e) => (e === j));
  }
  return mineArr;
}

function putMineInBoard(mine) {
  for (let i = 0; i < tdArr.length; i++) {
    if (findMine(i)) {
      tdArr[i].classList.add('mine');
    }
  }
  function findMine(j) {
    return mine.find((e) => (e === j));
  }
}



지뢰가 보드에 잘 자리 잡았다!!!




4. 보드의 가로, 세로 입력값에 따라 각 타일의 위치를 특정할 수 있는 식 세우기

보드의 크기가 고정된 형태로 게임을 구현하면 문제가 되지 않겠지만 보드의 크기를 입력값을 받아서 만들기 때문에 그에 따라 타일의 개수가 달라지고 그래서 타일의 위치를 파악할 수 있는 방법이 필요하다.

가로를 x 세로를 y 라고 잡았을 때
이런 식으로 대략적인 기준을 잡고 오른쪽으로 한 칸 이동하면 +1씩 아래칸으로는 +x씩 늘어난다는 정도만 잡아 놓으면 될 것 같다.

각 코너의 수들은 식으로 특정할 수 있지만 그 사이에 존재하는 칸들은 그 수가 변하기 때문에 반복문을 통해서 시작부분과 끝부분을 정하고 +1 혹은 +x를 해주면 될 것 같다.

위쪽 모서리: 1부터 시작 x-2까지 1씩 증가
아래쪽 모서리: xy-x부터 시작 xy-2까지 1씩 증가
왼쪽 모서리: x부터 시작 xy-2x까지 x만큼씩 증가
오른쪽 모서리: 2x-1부터 시작 xy-x-1까지 x만큼씩 증가

(getElementByClassName으로 만든 tdArr 변수에서 첫 번째 td는 tdArr.[0]칸에 위치해 있기 때문에 0번부터 시작하도록 만든다.)




5. 주변 타일들 배열로 만들기

타일을 선택했을 때 선택한 타일을 둘러싼 주변 타일들이 어떤 종류의 타일인지에 따라서 선택한 타일에서 실행할 내용이 달라진다.

이런 구분을 하기 위해선 우선적으로 특정 타일을 선택했을때의 주변 타일들을 그룹화 할 필요가 있었고 그 방법으로 배열에 담기로 했다.

주변 타일을 배열에 담는 작업을 하다보니 선택한 타일의 위치에 따라서 주변 타일의 개수와 관계식에 차이가 있었다.

🟥: 선택한 타일(N) / 🟦: 주변 타일 / x : 보드 가로 길이

각 코너 선택 시 주변 타일 그룹

왼쪽 위: [N+1, N+x, N+x+1]
오른쪽 위: [N-1, N+x-1, N+x]
왼쪽 아래: [N-x, N-x+1, N+1]
오른쪽 아래: [N-x-1, N-x, N-1]

각 모서리 선택 시 주변 타일 그룹

위쪽: [N-1, N+1, N+x-1, N+x, N+x+1]
아래쪽: [N-x-1, N-x, N-x+1,N-1, N+1]
왼쪽: [N-x, N-x+1, N+1, N+x, N+x+1]
오른쪽: [N-x-1, N-x, N-1, N+x-1, N+x]

나머지 타일들 선택 시 주변 타일 그룹

[N-x-1, N-x, N-x+1, N-1, N+1, N+x-1, N+x, N+x+1]




6. 각 타일에 클릭 시 실행할 함수 등록하기

function tileEvent(mine ,targetNum, ...aroundArr) {
  tdArr[targetNum].addEventListener("click", function () {
    let count = 0;
    for (let i = 0; i < aroundArr.length; i++) {
      if (findMine(aroundArr[i])) {
        count++
      }
    }
    
    function findMine(j) {
      return mine.find((e) => (e === j));
    }
        
    if (tdArr[targetNum].className === 'mine') {
      alert('GAME OVER!!!')
    } else if (count === 0) {
      tdArr[targetNum].style.backgroundColor = "darkcyan";
      for (let i = 0; i < aroundArr.length; i++) {
        tdArr[aroundArr[i]].click();
      }
    } else {
      tdArr[targetNum].innerHTML = count;
    }
  });
}

타일을 클릭 했을 때 어떤 작동을 할 지 세가지로 분류했다.

  1. 클릭한 타일이 class에 mine이 있을 때 = 지뢰 타일 => 게임 종료

  2. count(주변타일에 있는 지뢰 개수)가 0일 때 = 아무 정보 없는 타일 => 주변 타일 모두 실행

  3. count가 0이 아닐 때 = 숫자 타일 => 클릭한 타일에 count를 출력

함수를 생성하고 나서 각 코너, 모서리, 나머지 타일들 그룹별로 함수를 각각 실행했다.

  // 좌상
  tileEvent(mineArr, 0, 1, row, row+1);
  // 우상
  tileEvent(mineArr, row-1, row-2, 2*row-2, 2*row-1);
  // 좌하
  tileEvent(mineArr, row * (col-1), row*(col-2), row*(col-2)+1, row*(col-1)+1);
  // 우하
  tileEvent(mineArr, row*col-1, row*(col-1)-2, row*(col-1)-1, row*col-2);

  // 모서리 타일
  // 상
  for (let i = 1; i <= row-2; i++) {
    tileEvent(mineArr, i, i-1, i+1, i+row-1, i+row, i+row+1);
  }
  // 하
  for (let i = row*(col-1)+1; i <= row*col-2; i++) {
    tileEvent(mineArr, i, i-row-1, i-row, i-row+1, i-1, i+1);
  }
  // 좌
  for (let i = row; i <= row*(col-2); i += row) {
    tileEvent(mineArr, i, i-row, i-row+1, i+1, i+row, i+row+1);
  }
  // 우
  for (let i = 2*row-1; i <= row*(col-1)-1; i += row) {
    tileEvent(mineArr, i, i-row-1, i-row, i-1, i+row-1, i+row);
  }

  // 나머지 모든 타일
  for (let i = 1; i <= col-2; i++) {
    for (let j = i*row+1; j <= (1+i) * row-2; j++) {
      tileEvent(mineArr, j, j-row-1, j-row, j-row+1, j-1, j+1, j+row-1, j+row, j+row+1);
    }
  }

✔ Rest 파라미터

MDN - Rest 파라미터
코너, 모서리, 중앙 모두 각각의 주변 타일 그룹이 필요한데 배열을 다 따로 생성한 다음에 함수에 넣어야 하나 생각하고 있었는데 검색하다가 Rest 파라미터를 발견하고 바로 적용해 봤다.

🔨 변수의 유효 범위는 생각보다 많이 중요하다.

for 반복문을 처음 배울 때 변수를 초기화하는 부분에서 선언자 없이 그냥 쓴다고 잘못 기억하고선 지금까지 for 반복문을 쓸 때 선언자 없이 변수를 선언해왔다. 지금까지는 코드에 반복문을 여러 번 쓸 일이 없었기 때문에 괜찮았지만 이번에는 달랐다.

tileEvent 함수를 작성하고 나서 실행했을 때 원하는 결과가 안나와서 한 3일 동안을 그 부분을 해결한다고 골치를 썩었다. 도저히 혼자서는 답이 안나올 것 같아서 문제가 되는 부분을 wecode community에 질문 했는데 날아오는 답변 모두 'for 반복문 변수 선언에 선언자가 없어서 그런 것 같은데요?' 였다.

문제가 되는 부분 뿐만 아니라 그 위에서 부터 for 반복문을 쓸 때 모두 선언자 없이 변수 선언을 했기 때문에 변수가 어디서 어떻게 오염된 건지 찾기도 힘들 것 같았다.

결국 선언자 없이 변수를 선언했던 for 반복문에 전부 let 선언자를 추가하고 나서야 원하는 방향으로 코드가 돌아가기 시작했다.

🔨 find()를 indexOf()로 대체하기

MDN - Array.prototype.indexOf()

function setMineNumArr(numLimit, numRange) {
  let mineArr = [];
  for (let i = 0; i < numLimit; i++) {
    let randomNum = Math.floor(Math.random() * numRange);
    if (mineArr.indexOf(randomNum) === -1) {
      mineArr.push(randomNum);
    } else {
      i--;
    }
  }
  return mineArr;
}

function putMineInBoard(mine) {
  for (let i = 0; i < tdArr.length; i++) {
    if (mine.indexOf(i) !== -1) {
      tdArr[i].classList.add('mine');
    }
  }
}

function tileEvent(mine ,targetNum, ...aroundArr) {
  tdArr[targetNum].addEventListener("click", function () {
    let count = 0;
    for (let i = 0; i < aroundArr.length; i++) {
      if (mine.indexOf(aroundArr[i]) !== -1) {
        count++
      }
    }
    if (tdArr[targetNum].className === 'mine') {
      alert('GAME OVER!!!')
    } else if (count === 0) {
      tdArr[targetNum].style.backgroundColor = "darkcyan";
      for (let i = 0; i < aroundArr.length; i++) {
        tdArr[aroundArr[i]].click();
      }
    } else {
      tdArr[targetNum].innerHTML = count;
    }
  });
}



7. 마우스 우 클릭 기능 구현하기

우 클릭 > 깃발 > 우 클릭 > 물음표 > 우 클릭 > 원래 상태로

우 클릭 이벤트 리스너로 깃발과 물음표를 넣는 건 별로 어렵지 않을 것 같다. 문제는 우 클릭으로 타일을 깃발 타일과 물음표 타일로 만들었을 때 바꿔줘야 될 부분이 있다.

  • 깃발 타일에서 물음표 타일로 넘어가는 과정은 어떻게 구현할까?

    • 맨 처음 비활성된 타일을 우 클릭하면 깃발 타일이 된다.
    • 우 클릭시 기본으로 실행되는 메뉴를 없애기도 해야함(contextmenu, preventDefault)
    • 깃발 타일이 된 상태에서는 우 클릭을 했을 때 물음표 타일이 된다.
    • 그럼 우 클릭 이벤트를 실행할 때 해당 타일이 비활성화된 타일인지 깃발 타일로 변경된 타일인지 구분할 필요가 있다

  • 깃발 타일과 물음표 타일로 변환시 해당 타일에 설정된 좌 클릭 이벤트를 삭제해야한다. (깃발 타일과 물음표 타일은 좌 클릭에 반응하지 않도록)

    • 좌 클릭 이벤트를 삭제하는 대신에 좌 클릭 이벤트에 등록한 함수에 조건을 추가해서 우 클릭을 통해 추가된 class를 감지해 그 조건에서는 기능이 실행되지 않도록 구현
function tileEvent(mine, targetNum, ...aroundArr) {
  tdArr[targetNum].addEventListener("click", function () {
    if (tdArr[targetNum].className !== 'flag' && 
      tdArr[targetNum].className !== 'qmark' &&
      tdArr[targetNum].className !== 'mine flag' && 
      tdArr[targetNum].className !== 'mine qmark') {
      let count = 0;
      for (let i = 0; i < aroundArr.length; i++) {
        if (mine.indexOf(aroundArr[i]) !== -1) {
          count++
        }
      }
      if (tdArr[targetNum].className === 'mine') {
        alert('GAME OVER!!!')
      } else if (count === 0) {
        tdArr[targetNum].style.backgroundColor = "darkcyan";
        for (let i = 0; i < aroundArr.length; i++) {
          tdArr[aroundArr[i]].click();
        }
      } else {
        tdArr[targetNum].innerHTML = count;
      }
    }
  });

  tdArr[targetNum].addEventListener("auxclick", function () {
    tdArr[targetNum].addEventListener("contextmenu", function (e) {
      e.preventDefault();
    });

    if (tdArr[targetNum].className === 'flag' ||
      tdArr[targetNum].className === 'mine flag') {
      tdArr[targetNum].classList.remove('flag');
      tdArr[targetNum].classList.add('qmark');
      tdArr[targetNum].innerHTML = '❓'
    } else if (tdArr[targetNum].className === 'qmark' ||
      tdArr[targetNum].className === 'mine qmark') {
      tdArr[targetNum].classList.remove('qmark');
      tdArr[targetNum].innerHTML = '';
    } else {
      tdArr[targetNum].classList.add('flag');
      tdArr[targetNum].innerHTML = '🚩';
    }
  });
}

✔ auxclick

JS의 이벤트에 우 클릭에 해당하는 이벤트는 따로 없는 것 같다. auxclick이라는 이벤트는 (click 이벤트가 좌 클릭에 해당하는 것에 반해서) 좌 클릭을 제외한 마우스의 클릭 이벤트를 의미한다.

MDN - Element: auxclick event

✔ contextmenu, preventDefault()

페이지 상에서 우 클릭시 기본적으로 실행되는 기능을 없앨 방법이 필요했다.

MDN - Element: contextmenu event
MDN - event.preventDefault

noContext = document.getElementById('noContextMenu');

noContext.addEventListener('contextmenu', e => {
  e.preventDefault();
});



8. 주변 타일 중 깃발 타일 개수와 지뢰 타일 개수가 같을 때 숫자타일 클릭시 나머지 타일 여는 기능 구현하기

숫자타일에 클릭 or 더블클릭 이벤트 설정



9. 숫자 별로 색 다르게 하기

배열 COLOR를 선언하고 타일에 숫자 넣는 과정에서 배열 인덱스로 접근해서 color 다르게 주기



10. 맨 처음 선택하는 타일은 절대 지뢰가 아니다?

지뢰찾기 게임에서 제일 처음 선택하는 타일은 절대 지뢰가 나오는 경우가 없다 근데 이걸 어떻게 구현하지???



🤔 프로그램 한계 / 더 생각해 보기

프로그램 멈춤 현상 > 원인

주변 타일을 click()을 통해 연쇄적으로 실행하는 과정에서 이미 실행되서 열린 타일들을 구분없이 계속해서 click()하면서 call stack이 초과 되었다고 한다. 프로그램이 멈추는 원인을 몰라서 프로젝트를 마무리 하지 못하고 미뤘는데 이렇게 원인을 파악해서 알려주시니 그저 감사할 따름... 누군가 내 깃허브에 방문해서 코드를 봐주는 신선한 경험을 했다



github - 전체 코드

profile
즐거웁게 코딩합시다.
post-custom-banner

0개의 댓글