자바스크립트 DOM(Document Object Model) 조작과 이벤트 핸들링(Event Handling)

DOM

  • Document Object Model, 문서 객체 모델
    • HTML을 객체화해서 HTML 요소에 접근하고 조작할 수 있도록 한다.
    • ORM 같은 개념…
  • DOM Tree
    • 각 HTML 요소들은 트리 구조로 구조화되어서 하나의 객체가 된다.
    • 돔 트리는 브라우저에서 HTML이 로드될 때 생성한다.
  • document.querySelector('');
    • document가 각 요소에 접근할 수 있는 entry point가 된다.
    • CSS 선택자 규칙을 바탕으로 문서의 요소를 선택할 수 있다.
  • DOM 프로퍼티와 DOM을 조작하는 DOM 메서드들은 Web API 라이브러리이다.
    • 해당 API가 JS를 지원하므로 JS를 사용해서 웹 개발을 하는 것이 널리 퍼지게 된 것

숫자 맞추기 게임 만들기

HTML Element 조작하기

document.querySelector('.number').textContent = 13;
document.querySelector('.score').textContent = 10;

document.querySelector('.guess').value = 23;
console.log(document.querySelector('.guess').value);
  • textContent 프로퍼티로 텍스트를 변경할 수 있다.
  • value 프로퍼티로 input의 value 값을 변경할 수 있다.

클릭 이벤트 핸들링

const secretNumber = Math.trunc(Math.random() * 20) + 1;
let score = 20;
document.querySelector('.number').textContent = secretNumber;

document.querySelector('.check').addEventListener(
  'click', function () {
    const guess = Number(document.querySelector('.guess').value);
    if (!guess) {
      document.querySelector('.message').textContent = '👋 No number!';
    } else if (guess === secretNumber) {
      document.querySelector('.message').textContent = '🎉 Correct Number!';
    } else if (guess > secretNumber) {
      if (score > 1) {
        document.querySelector('.message').textContent = 'Too high!';
        score--;
        document.querySelector('.score').textContent = score;
      } else {
        document.querySelector('.message').textContent = 'You lost the game! 😋';
        document.querySelector('.score').textContent = 0;
      }
    } else if (guess < secretNumber) {
      if (score > 1) {
        document.querySelector('.message').textContent = 'Too Low!';
        score--;
        document.querySelector('.score').textContent = score;
      } else {
        document.querySelector('.message').textContent = 'You lost the game! 😋';
        document.querySelector('.score').textContent = 0;
      }
    }
  });
  • addEventListener의 첫번째 인자로 'click', 두 번째 인자로 실행할 함수를 작성한다.
  • let은 state variable
    • application 상태에 따라 수명이 결정된다.
    • 화면이 reload 되면 값도 새로 할당된다.

CSS 조작하기

document.querySelector('body').style.backgroundColor = '#60b347';
document.querySelector('.number').style.width = '30rem';
  • style 프로퍼티를 붙여서 요소의 style 속성을 수정할 수 있도록 한다.
  • CSS 속성은 카멜 케이스로 작성한다.
  • 값은 항상 문자열로 작성한다. (숫자만 있더라도 문자열로)
  • 적용되면 HTML 요소에 style 속성이 들어가는 것을 볼 수 있다.

모달 창(Modal Window) 만들기

<body>
  <button class="show-modal">Show modal 1</button>
  <button class="show-modal">Show modal 2</button>
  <button class="show-modal">Show modal 3</button>

  <div class="modal hidden">
    <button class="close-modal">&times;</button>
    <h1>I'm a modal window 😍</h1>
    <p>
      Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
      tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
      veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
      commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
      velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
      occaecat cupidatat non proident, sunt in culpa qui officia deserunt
      mollit anim id est laborum.
    </p>
  </div>
  <div class="overlay hidden"></div>

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

querySelector vs. querySelectorAll

const modal = document.querySelector('.modal');
const overlay = document.querySelector('.overlay');
const btnCloseModal = document.querySelector('.close-modal');
const btnsOpenModal = document.querySelectorAll('.show-modal')
  • querySelector의 경우 첫번째 요소만 반환하므로, 모든 요소를 선택하려면 querySelectorAll을 사용한다.
  • querySelectorAll는 NodeList를 반환
for (let i = 0; i < btnsOpenModal.length; i++) console.log(btnsOpenModal[i].textContent)
  • if문과 마찬가지로 for문 역시 한문장으로 끝나는 코드는 {} 생략 가능

classList

const openModal = function () {
  modal.classList.remove('hidden');
  overlay.classList.remove('hidden');
};

const closeModal = function () {
  modal.classList.add('hidden');
  overlay.classList.add('hidden');
};

for (let i = 0; i < btnsOpenModal.length; i++)
  btnsOpenModal[i].addEventListener('click', openModal);

btnCloseModal.addEventListener('click', closeModal);
overlay.addEventListener('click', closeModal);
  • 모달 요소마다 클릭 이벤트 리스너를 붙이고, 클릭 후에는 숨겨 놓은 콘텐츠를 보이게 하기 위해 클래스 리스트를 불러와서 hidden 클래스를 없앤다.
  • modal.style.display = 'block';으로 hidden을 보이게 하면 안될까?
    • 그렇게 해도 동작은 하겠지만, 많은 속성을 수정해야 하는 경우 등의 이슈가 있을 때는 원하는 스타일을 스타일 시트에 미리 작성해놓고 클래스를 remove하거나, add 하는 방식이 효율적일 수 있다.

키보드 이벤트 핸들링

  • 키보드 이벤트는 특정 요소에 동작하는 것이 아니기 때문에 global event라고 부른다.
  • 종류
    • document.addEventListener('keyup')
    • document.addEventListener('keypress')
    • document.addEventListener('keydown')
document.addEventListener('keydown', function (e) {
  if (e.key === 'Escape' && !modal.classList.contains('hidden')) {
    closeModal();
  }
});
  • function 파라미터로 이벤트 객체를 받아와서 활용할 수 있다.
    • KeyboardEvent {isTrusted: true, key: 'e', code: 'KeyE', location: 0, ctrlKey: false, …}

주사위 게임 만들기

getElementById

const score0El = document.querySelector('#score--0');
const score1El = document.getElementById('score--1');
  • 요소의 ID를 선택시에는 querySelector 대신 getElementById를 사용할 수 있다.

classList.toggle

btnRoll.addEventListener('click', function () {
  // 1. 랜덤 다이스 생성
  const dice = Math.trunc(Math.random() * 6) + 1;
  // 2. 다이스 디스플레이
  diceEl.classList.remove('hidden');
  diceEl.src = `dice-${dice}.png`;

  // 3. 다이스가 1이면 상대방에게 넘긴다.
  if (dice !== 1) {
    // currentScore에 다이스 점수 더하기
    currentScore += dice;
    document.getElementById(`current--${activePlayer}`).textContent = currentScore;
  } else {
    // 상대방으로 넘기기
    document.getElementById(`current--${activePlayer}`).textContent = 0;
    currentScore = 0;
    activePlayer = activePlayer === 0 ? 1 : 0;
    player0El.classList.toggle('player--active');
    player1El.classList.toggle('player--active');
  }
});
  • 해당 클래스가 있으면 없애고(remove), 없으면 더한다(add).

init

let scores, currentScore, activePlayer, playing;

const init = function () {
  scores = [0, 0];
  currentScore = 0;
  activePlayer = 0;
  playing = true;

  score0El.textContent = 0;
  score1El.textContent = 0;
  current0El.textContent = 0;
  current1El.textContent = 0;
  player0El.classList.remove('player--winner');
  player1El.classList.remove('player--winner');
  player0El.classList.add('player--active');
  player1El.classList.remove('player--active');

  diceEl.classList.add('hidden');
};
init();

btnNew.addEventListener('click', init);
  • 파일이 로드 될 때 init 함수를 한번 실행
  • 파일 전체에서 접근할 수 있도록 함수 밖에서 변수 선언
  • 이벤트 리스너에 함수 자체를 넘겨서 실행시킬 수 있음

Leave a Reply

Your email address will not be published. Required fields are marked *