[JavaScript] 이벤트 객체를 꼼꼼히 정리해보자.

김유진·2023년 3월 12일
0

Javascript

목록 보기
16/22
post-thumbnail

지금까지 이벤트 핸들러에 대한 코드를 작성할 때 이벤트 핸들러가 어떤 역할을 하는지 잘 알지 못한 채로 이벤트 객체 e를 사용하였는데, 이번 글을 통하여 이벤트 핸들러와 이벤트는 어떻게 일어나는지 정리해보고자 한다.

1. 이벤트 객체가 무엇일까?

DOM과 관련된 이벤트가 발생하면 해당 이벤트에 관련된 모든 정보가 이벤트 객체에 저장된다. 이벤트 발생 요소, 이벤트에 대한 타입, 이벤트 관련된 데이터가 모두 저장된다.

이벤트의 종류는 정말 많다. click, scroll , transition 등 일일이 암기하기 어렵다. 자주 쓰이는 type 정도를 암기하고, 필요할 때는 그때 그때 찾아 쓰는 것도 하나의 방법이 될 수 있다.

2. 이벤트 객체를 사용하여 간단한 예제를 만들어보자.


사진은 일분코딩님의 캐릭터 일분이를 이용한 것이다.

첫번째 시도

해당 캐릭터를 가지고 있는 DOM 요소를 가져와서 이벤트핸들러를 가져오는 방식으로 작성을 해보자.

window.addEventListener('load', function() {
  const ilbuni3load = document.querySelector('.ilbuni.c')
  function clickInbuniHandler(){
     ilbuni3load.classList.toggle('special');
  }
  ilbuni3load.addEventListener('click', clickInbuniHandler);
});

이렇게 작성하면 해당 조건에 해당하는 class를 documnet요소로 가져오고, special이라는 css 클래스가 있을 때는 추가를 해주고, 없을 때는 special 클래스 이름을 추가해준다.
addEventListener의 첫번째 요소에는 event 종류가, 두번째에는 해당 이벤트가 일어났을 때 수행할 함수의 이름을 작성한다.
그런데 이렇게 작성하면 좋은 코드라고 말하기 어렵다.

load라는 DOM 연산은 많은 시간을 소요하기 때문이다. DOM을 하나하나씩 다시 load하고 검색하여 해당 연산을 수행하는 것은 메모리 상에 큰 낭비를 일으킨다.

두 번째 시도

(function(){
    const ilbuni3load = document.querySelector('.ilbuni.c');
    function clickInbuniHandler(){
       ilbuni3load.classList.toggle('special');
    }
    ilbuni3load.addEventListener('click', clickInbuniHandler);
})();

전체 DOM이 로드 된 이후, 이벤트 핸들러 함수를 작성하는 방식으로 개선할 수 있다.
굳이 해당 스크립트를 사용하겠다고 load연산을 사용한 뒤에 함수를 작성할 필요가 없다는 것이다.
모든 DOM이 로드된 이후, 해당 요소를 querySelector를 이용하여 불러온 이후 addEventListener 함수를 작성하면 되는 것이다.

function() 함수와 같이 익명함수를 왜 작성하려는 것일까?
그것은 바로 전역변수의 남발을 막기 위해서이다.
let과 const같은 변수는 block이 끝나면 유효함도 끝나게 되므로 전역변수로 여러 곳에서의 이용을 막을 수 있다.

그런데 해당 이벤트를 수행하려고 할 때 이벤트 객체를 이용해보는 것은 어떨까?

마지막 시도

(function(){
   const characters = document.querySelector('.characters');
   function clickHandler(e){
      console.log(this); //this는 characters임.
      //addeventListen를 호출한 주체
      console.log(e);
      console.log(e.target); //내가 정말 클릭한 부분!
      // this == e.currentTarget!
}
   characters.addEventListener('click', clickHandler)
})();

이벤트 객체를 불러오는 함수에서 this는 무엇을 가리키는 것일까? 여기서의 thisaddEventListener 메소드를 호출한 주체인 characters가 된다.
여기서 e를 출력하면 정말 다양한 종류들이 나오는데 맛보기로 console의 내용을 지켜보자.

발생한 이벤트의 종류와 실제로 내가 클릭한 곳의 좌표가 뜨게 된다.
this를 출력한 결과를 보게 되면, 전체 container인 characters가 뜨게 되는데, 정말로 내가 클릭한 그 주체만을 알고 싶다면 e.target을 사용하면 된다.

참고로 알고 있으면 좋은 점은 this가 바로 e.currentTarget을 의미한다.

3. 클릭하면 캐릭터가 없어지는 게임 만들기!

일분코딩님의 강의를 참고해서 이동하는 캐릭터 중 클릭하면 그 캐릭터가 사라지는 예제를 한번 만들어 보자.

(function(){
     const ilbuniGroup = document.querySelectorAll('.ilbuni');
     const stage = document.querySelector('.stage');
     function clickHandler(e){
        stage.removeChild(this);
        // this.parentNode.removeChild(this); 이렇게해도됨.
     }
     for (let i = 0 ; i < ilbuniGroup.length; i++){
        ilbuniGroup[i].addEventListener('click',clickHandler);
     }
})();

ilbuni태그를 모두 선택하여 ilbuniGroup을 만들고, 그 안에서 for문을 돌리면서, click시 clickHandler가 실행되는 구조이다. ilbuniGroup[i]의 부모에서 클릭된 요소를 지우는 방식이므로, this.parentNode.removeCHild(this)로 해도 되고, stage.removeChild로 진행해도 된다.

더 좋은 코드?

function(){
    const stage = document.querySelector('.stage');
    function clickHandler(e){
        if (e.target.classList.contains('ilbuni')) {
          stage.removeChild(e.target);
        }}
    stage.addEventListener('click', clickHandler)
})();

이렇게 작성하면 이벤트 위임을 잘 사용하여 작성한 코드라고 할 수 있다.
먼저 전체 stage에다가 addEventListener함수를 작성해주고, stage의 target이 된 대상이 ilbuni라는 클래스를 가지고 있을 때만 해당 타겟을 전체 부모 container에 해당하는 요소에서 지워 주는 것이다.
if문을 통하여 ilbuni가 아니라 다른 stage의 요소를 선택하였을 때 발생하는 짜잘한 에러를 방지할 수 있다는 점이 중요하다.

4. 클릭한 곳으로 공을 움직여 보자!

const ballElem = document.querySelector('.ball');
    window.addEventListener('click', function(e) {
      ballElem.style.transform = 'translate(' + e.clientX + 'px, ' + e.clientY + 'px)'
    });
    ballElem.addEventListener('transitionend', function(e){
      ballElem.classList.add('end');
    })

ball 요소를 DOM으로부터 가져온 이후, 전체 window 객체에서 click이벤트가 발생하였을 때 해당 위치로 transform을 이용하여 이동시키는 이벤트이다.
clientXclientY를 통하여 클라이언트가 실제로 클릭한 곳이 어디인지 그 값을 받고, translate로 실제로 이동을 시켜준다!

그리고 transitionend를 통하여 transition이 모두 끝나고나서 실행할 이벤트를 지정할 수 있는데, end클래스가 추가되면 파란색 공으로 변하도록 한 것이다.
이 때, e.elapsedTimee.propertyName을 찍어 보았는데,

transition 시간으로 설정해둔 0.5s와 발생하는 이벤트 이름인 transformbackground가 실행되었다.

애니메이션에도 이벤트 함수를 등록할 수 있어요.

@keyframes ball-ani {
	from {
		transform: translate(0, 0);
	}
	to {
		transform: translate(200px, 200px);
	}
}

위와 같은 ball-ani 애니메이션이 존재한다고 생각해보자. 클릭하였을 때 애니메이셔닝 실행되도록 하고 싶다면 이벤트 함수를 어떻게 등록해야 할 지 고민하지 않아도 된다.

const ballElem = document.querySelector('.ball');
window.addEventListener('click', function (e) {
   ballElem.style.animation = 'ball-ani 1s 3 forwards';
});

click을 하였을 때, ball-ani함수가 3번 수행되도록 한다.
애니메이션이 3번 반복되니까, 반복될 때마다 특정한 행동을 하도록 만들고 싶다면 아래 함수와 같이 작성할 수도 있을 것이다.

ballElem.addEventListener('animationiteration', function (e) {
        console.log('반복!');
    });

0개의 댓글