본문 바로가기
Programming/JavaScript

이벤트, 이벤트 핸들링 (Event Handler) - [Javascript _ 12]

by Muko 2020. 4. 30.

오늘은 이벤트와 이벤트 핸들링에 대해서 포스팅 하려고 합니다.

1. 이벤트 (event)

이벤트는 브라우저 상에서 일어나는 어떤 사건을 의미합니다. 여기서 사건은 사용자가 클릭을 했거나, 스크롤을 했을 때, 키보드를 눌렀을 때와 키보드에서 손을 뗐을 때, 입력 값을 제출했을 때, 마우스를 움직이고 있을 때 등을 의미합니다. 이벤트가 DOM에만 한정되는 것은 아니지만, 모든 DOM 노드는 이런 신호들을 만들어 낼 수 있습니다.

자주 사용되는 유용한 DOM 이벤트 종류를 소개하겠습니다.

마우스 이벤트:

  • click - DOM 요소 위에서 마우스로 클릭(스마트폰에서는 탭)했을 때 발생
  • contextmenu - 요소 위에서 마우스 오른쪽 버튼 클릭했을 때 발생
  • mouseover - 마우스 커서를 요소 위에 두었을 때 발생
  • mouseout - 마우스 커서를 요소 안에서 밖으로 움직였을 때 발생
  • mousedown - 요소 위에서 마우스로 클릭하고 있을 때 발생
  • mouseup - 요소 위에서 클릭하고 있던 마우스를 떼면 발생
  • mousemove - 마우스를 움직일 때 발생

Form 요소 이벤트:

  • submit - 사용자가 <form>을 제출할 때 발생
  • focus - <input> 등의 요소에 포커스를 할 때 발생 (예를 들면 입력 요소에서 커서 깜빡거리고 있는 부분)

키보드 이벤트:

  • keydown - 사용자가 키보드 버튼을 누를 때 발생
  • keyup - 누르고 있던 키보드 버튼을 뗄 때 발생

Document 이벤트:

  • DOMContentLoaded - HTML이 전부 로드된 후 처리되어 DOM 생성이 완료된 순간 발생

CSS 이벤트:

  • transitioned - CSS 애니메이션이 종료되었을 때 발생



2. 이벤트 리스너 (Event Listener)

<!DOCTYPE html>
<html>
  <body>
    <input type="button" onclick="alert(window.location)" value="alert(window.href)" />
    <div style="width=200px; height: 300px;" onmouseover="alert('hi!')">
      <p>마우스를 올려보세요</p>
    </div>
  </body>
</html>

마우스를 올려보세요

 

onclick 속성으로 들어가 있는 alert(window.location) 은 사용자가 해당 버튼을 클릭했을 때 발생하고, 그 아래 마우스를 올려보세요 를 감싸고 있는 div 태그에 onmouseover 속성에 있는 `alert('hi!')`도 마찬가지로 사용자가 마우스를 해당 요소 위로 옮겼을 때 발생합니다. 즉, 프로그래머는 어떤 사건이 발생했을 때 실행 되어야하는 코드를 등록하고, 브라우저 혹은 프로그램이 그 일이 발생했을 때 등록된 코드를 실행하게 되는 구조입니다.

위의 예시에서 보이듯, 어떤 이벤트에 대해서 대기 중인 기능을 이벤트 리스너라고 합니다.

window.onload = () => {
  alert('This page is loaded');
};

이 코드는 window가 로딩되었을 때에 실행되는 이벤트 리스너입니다. 따라서 window가 로딩이 되기 전까지는 계속해서 대기 중이었다가, 로딩이 완료되었다는 신호가 발생하면(이벤트 발생) 'This page is loaded'라는 문자열을 출력하는 알람창이 나오게 됩니다. 이렇게 이벤트 리스너를 달 수 있는 속성 이름은 on + '이벤트명'으로 정해져 있습니다. 그래서 위에 mouseover, submit 등의 이벤트에 이벤트리스너를 등록할 때는 'onmouseover', 'onsubmit' 등의 속성에 연결해야합니다.

만약 HTML의 속성값으로 이벤트 리스너를 등록할 때, 동작해야 하는 코드가 길다면 따로 함수로 분리하는 것이 관리하기도 편하고 이해하기도 편합니다.

<script>
  const names = ['lee', 'kim', 'kang'];
  const printNames = () => {
    names.forEach(elem => {
      alert('Name is '+elem);
    });
  };
</script>
<input type="button" onclick="printNames()" value="내가 가진 이름들 출력" />

 

여기서 아시면 좋은 팁! HTML 속성은 대소문자를 구분하지 않기 때문에 ONCLICK과 onClick, onclick 모두 동일하게 동작합니다. 하지만 일반적으로 속성값을 작성할 때는 소문자로 작성하니 준수하는 것을 추천합니다.

 

3. DOM 프로퍼티

이벤트 핸들러는 HTML 속성 뿐만 아니라 DOM 프로퍼티에도 할당할 수 있습니다. 위에서 설명한 것과 같이 on<event>의 형식으로 속성에 추가할 수 있습니다. 또한 이벤트 핸들러는 언제나 DOM 프로퍼티에 할당됩니다. HTML 속성을 이용해서 이벤트 핸들러를 정의하는 방법은 DOM 프로퍼티를 초기화하는 여러 방법 중 하나입니다. 따라서 2번에서 설명한 방식도 결국에는 DOM 프로퍼티에 할당하기 위한 방법이라고 이해하시면 됩니다.

1. HTML만 사용하는 방법

<input type="button" onclick="alert('클릭되었습니다.')" value="클릭해주세요!"/>

 

2. HTML과 자바스크립트를 같이 사용하는 방법

<input type="button" id="button" value="클릭!" />
<script>
button.onclick = () => {
 alert('클릭되었습니다.');
};
</script>

 

여기서 주의하셔야 할 것이 두가지가 있습니다.
우선, 첫 번째는 onclick과 같이 프로퍼티는 하나밖에 없기 때문에 여러개의 이벤트 핸들러를 할당할 수 없다는 것입니다. 만약 onclick 프로퍼티에 이벤트 핸들러를 할당했었는데, 나중에 다시 할당하게되는 구조라면 처음에 할당했었던 이벤트 핸들러는 없어지게 되는 것이죠. 그래서 이러한 특징을 이용하면 프로퍼티에 elem.onclick = null과 같이 할당된 값을 없애서 이미 할당된 이벤트 핸들러를 없애는 것이 가능해집니다.

두 번째는 DOM 프로퍼티에 이벤트 핸들러를 할당할 때 함수를 할당해야지, 함수의 호출값을 할당하면 안된다는 것입니다. 예를 들어 button.onclick = printHi;와 같이 할당해야 정상적으로 동작하는데, button.onclick = printHi(); 처럼 함수를 호출해서 넣는 것이죠. 우리는 이벤트가 발생했을 때 동작해야하는 함수 그 자체를 할당해주어야 하기 때문에 괄호 없이 함수를 할당해야 합니다.

 

4. addEventListener

위에서 HTML을 이용하거나 DOM 프로퍼티를 이용해서 이벤트 리스너를 추가하는 방법을 알아보았습니다. 이 때 '여러개의 이벤트'에 반응할 수 없다는 문제와 그 이유를 설명했습니다. 그런데 여러 이벤트에 반응을 해야만 하는, 혹은 반응을 하게끔 만들고 싶다면 어떻게 해야할까요? 예를 들면 버튼을 클릭했을 때의 반응과, 버튼 위에 마우스를 올렸을 때, 그리고 버튼 위에서 마우스를 옮겨서 다른 곳에 올렸을 때 등의 이벤트 상황에 맞게 반응하게끔 해야하는 상황이라면 말이죠.

이 때 사용하면 유용한 것이 바로 addEventListener 입니다. 하나의 DOM 노드에 여러개의 이벤트를 추가하는 것이 가능합니다. 물론 removeEventListener를 이용해서 삭제하는 것도 가능합니다. 문법은 다음과 같습니다.

EventTarget.addEventListener(event, handler [, options]);

여기서 event는 1번에서 설명한 이벤트 종류의 이름을 적습니다. 예를 들면 click, mouseover 등이 있습니다. 그 다음 handler 부분은 이벤트가 발생했을 때 동작할 함수를 의미합니다. 보통 이벤트를 다루는 함수라고 해서 이벤트 핸들러라고 부릅니다. 여기서는 함수를 넣을 때 호출값을 보내는 것이 아니라 함수 자체를 보내는 것이기 때문에 괄호를 붙이지 않습니다. 마지막으로 options는 생략가능한 부분인데 아래 세 가지 프로퍼티를 지니는 객체가 들어갑니다.

  • once: 값이 true일 경우 이벤트가 발생했음을 감지했을 때 이벤트 리스너가 자동으로 삭제됩니다.
  • capture: 어떤 단계에서 이벤트를 다룰지 설정하는 프로퍼티입니다. 자세한 내용은 버블링과 캡쳐링 포스팅에서 다루겠습니다.
  • passive: 값이 true일 경우 이벤트 리스너에서 지정한 이벤트 핸들러가 preventDefault()를 호출하지 않습니다. 이 부분은 브라우저 기본 동작 포스팅에서 더 다루겠습니다.

 

삭제할 때 사용하는 removeEventListener 또한 문법은 동일합니다. 다만 삭제를 할 때 주의해야 하는 것은 동일한 함수를 사용해야 한다는 것입니다. 만약 이벤트 리스너에 등록한 이벤트 핸들러를 추가할 때 마다 새로 정의해서 넣었을 경우, 원하는 것 처럼 삭제되지 않습니다. 아래 예시와 같이요!

 

element.addEventListener("click", () => alert("안녕하세요!"));
element.removeEventListener("click", () => alert("안녕하세요!"));

그러면 원하는 것 처럼 삭제를 하려면 어떻게 해야할까요? 이벤트 핸들러를 지정할 때마다 함수를 새로 생성해서 배정하지 않고, 먼저 함수를 선언해 놓은 상태에서 추가하거나 삭제를 하면 됩니다.

const sayHello = () => {
  alert("안녕하세요~");
};

element.addEventListener("click", sayHello);
element.removeEventListener("click", sayHello);

그럼 이제 addEventListener를 이용해서 여러개의 이벤트 핸들러를 추가해보도록 하겠습니다.

<input id="myBtn" type="button" value="클릭!" />

<script>
  const sayHello = () => {
    alert('안녕하세요!');
  };
  const sayThanks = () => {
    alert('감사합니다!');
  };

  const myBtn = document.querySelector('#myBtn');
  myBtn.addEventListener('click', sayHello);
  myBtn.addEventListener('click', sayThanks);
</script>



그런데 DOMContentLoaded 이벤트는 addEventListener를 사용해야만 합니다.

document.addEventListener('DOMContentLoaded', () => {
  alert('DOM이 준비되었습니다.');
});

 

5. 이벤트 객체

위에서는 이벤트 리스너를 통해서 어떤 이벤트가 발생했을 때에 따라 동작할 수 있는 이벤트 핸들러를 다는 방법에 대해서 알아보았습니다. 그런데 이벤트 핸들런 내에서 이벤트를 제대로 다루기 위해서는 지금 브라우저에서 어떤 일이 발생하고 있는지를 파악할 수 있다면 훨씬 유용하겠죠? 현재 이벤트가 발생한 부분이 어떤 DOM 객체인지, 현재 발생한 이벤트가 어떤 이벤트인지, 마우스 클릭 이벤트가 발생했을 경우 마우스 포인터의 좌표값이 어디인지 등 여러가지 정보가 필요할 수 있습니다. 이 때 우리가 사용할 수 있는 것이 바로 이벤트 객체 입니다.

브라우저에서 이벤트가 발생하게되면 브라우저가 이벤트 객체를 생성합니다. 발생한 이벤트와 관련된 정보들을 이 객체에 담은 뒤, 이벤트 핸들러에 인수 형태로 전달하게끔 되어있습니다. 아래 예시를 보실까요?

<input id="myBtn2" type="button" value="클릭!">
<script>
  myBtn2.onclick = (event) => {
    alert(event.type + ' 이벤트가 ' + event.currentTarget + '에서 발생했습니다.');
  };
</script>

 

이벤트 객체에는 정말 많은 정보가 담겨있지만, 그 중 위에서 사용한 것들에 대해서 설명을 하자면 event.type은 발생한 이벤트가 어떤 것인지에 대한 정보를 가지고 있습니다. 그리고 event.currentTarget은 해당 이벤트가 발생한, 혹은 이벤트를 처리하는 요소에 대한 정보를 가지고 있습니다.

 

여기까지 따라오시느라 고생하셨습니다.
다음 포스팅에서는 버블링에 대해서 알아보도록 하겠습니다.

댓글3