JavaScript

[Javascript] Event

prefer2 2022. 3. 20. 01:30

 

이벤트 핸들러


이벤트에 반응하려면 이벤트가 발생했을 때 실행되는 함수인 핸들러(handler)를 할당해야 한다.

핸들러는 사용자의 행동에 어떻게 반응할지를 코드로 표현한 것이다.

 

addEventListener

HTML에 직접 이벤트 핸들러를 등록하는 방법은 단 1개의 이벤트만을 등록할 수 있다. 또한 코드가 길어져 가독성이 떨어지게 된다. addEventListener를 사용하면 복수의 핸들러를 등록할 수 있다.

element.addEventListener(event, handler, [options]);
  • event: 이벤트 이름. “click”, “scroll” 등
  • handler: 핸들러 함수
  • option
    • once: true이면 이벤트가 트리거 될 때 리스너가 자동으로 삭제된다.
    • capture: 어느 단계에서 이벤트를 다뤄야 하는지를 알려주는 프로퍼티
    • passive: true이면 리스너에서 지정한 함수가 preventDefault()를 호출하지 않는다.

 

event 객체

이벤트가 발생하면 브라우저는 이벤트 객체(event object)를 만든다. 여기에 이벤트에 관한 상세한 정보를 넣은 다음, 핸들러에 인수 형태로 전달한다.

<input type="button" value="클릭해 주세요." id="elem">

  <script>
    elem.onclick = function(event) {
      console.log(event)
      console.log(event.type + " 이벤트가 " + event.target + "에서 발생했습니다.");
      console.log("이벤트가 발생한 곳의 좌표는 " + event.clientX + ":" + event.clientY +"입니다.");
    };
  </script>

 

<button id="elem">클릭해 주세요.</button>

  <script>
    class Menu {
      handleEvent(event) {
        let method = 'on' + event.type[0].toUpperCase() + event.type.slice(1);
        this[method](event);
      }

      onMousedown() {
        elem.innerHTML = "마우스 버튼을 눌렀습니다.";
      }

      onMouseup() {
        elem.innerHTML = "그리고 버튼을 뗐습니다.";
      }
    }

    let menu = new Menu();
    elem.addEventListener('mousedown', menu);
    elem.addEventListener('mouseup', menu);
  </script>

위와 같이 메서드를 사용해서 이벤트를 관리해 줄 수도 있다. 이벤트 핸들러만을 따로 모으기 좋은 방법인 것 같다.

 

this

addEventListener()를 사용해 요소에 수신기를 부착하게 되면 수신기 내부의 this 값은 대상 요소를 가리키게 되며, 이는 수신기가 매개변수로 받게 되는 이벤트 객체의 currentTarget 속성과 같습니다. -MDN-

 

<button id="btn">클릭해주세요!</button>
  <script>
    document.querySelector('#btn').addEventListener('click', onClick)
    function onClick(e) {
      console.log(e.currentTarget === this) //true
    };
  </script>
  • event.target은 실제 이벤트가 시작된 ‘타깃’ 요소이다. 버블링이 진행되어도 변하지 않는다.
  • this는 ‘현재’ 요소로, 현재 실행 중인 핸들러가 할당된 요소를 참조한다.

화살표 함수로 작성되었을 경우 화살표 함수의 규칙을 따르게 된다. 따라서 아래 예시에서는 결과로 window가 나오게 된다.

<button id="btn">클릭해주세요!</button>
 <script>
   document.querySelector('#btn').addEventListener('click', (e)=>{
     console.log(this)
   })
 </script>

 

이벤트 위임


버튼의 개수가 계속 늘어나는 경우 모든 버튼에게 이벤트 핸들러를 등록하는 것은 꽤나 복잡하고 코드가 길어질 것이다. 이벤트 위임을 이해하면 조상 요소에게만 이벤트를 할당하여 자손 요소들의 이벤트를 쉽게 관리할 수 있다.

이벤트 위임을 이해하기 위해서는 우선 버블링(bubbling)을 이해해야한다.

 

버블링

한 요소에 이벤트가 발생하면, 이 요소에 할당된 핸들러가 동작하고, 이어서 부모 요소의 핸들러가 동작한다. 가장 최상단의 조상 요소를 만날 때까지 이 과정이 반복되면서 요소 각각에 할당된 핸들러가 동작한다.

<style>
  body * {
    margin: 10px;
    border: 1px solid blue;
  }
</style>

<form onclick="alert('form')">FORM
  <div onclick="alert('div')">DIV
    <p onclick="alert('p')">P</p>
  </div>
</form>
  1. <p>에 할당된 onclick 핸들러가 동작
  2. 바깥의 <div>에 할당된 핸들러가 동작
  3. 그 바깥의 <form>에 할당된 핸들러가 동작
  4. document 객체를 만날 때까지, 각 요소에 할당된 onclick 핸들러가 동작

버블링 중단

이벤트 객체의 메서드인 event.stopPropagation() 를 사용하여 버블링을 중단할 수 있다. 하지만 이는 여러가지 문제를 발생시킬 수 있기 때문에 정말 필요한 경우를 제외하고는 사용하지 않도록 하자. 이벤트 버블링을 막을 경우는 거의 없고 이또한 커스텀 이벤트를 통해 해결 할 수 있다.

 

이벤트 위임(event delegation)

이벤트 위임을 사용하면 각 요소에 이벤트를 할당하지 않고 조상에게 이벤트를 할당하고 자손 요소들을 한꺼번에 조작할 수 있다.

<div id="menu">
  <button data-action="save">저장하기</button>
  <button data-action="load">불러오기</button>
  <button data-action="search">검색하기</button>
</div>

<script>
  class Menu {
    constructor(elem) {
      this._elem = elem;
      elem.onclick = this.onClick.bind(this); // (*)
    }

    save() {
      alert('저장하기');
    }

    load() {
      alert('불러오기');
    }

    search() {
      alert('검색하기');
    }

    onClick(event) {
      let action = event.target.dataset.action;
      if (action) {
        this[action]();
      }
    };
  }

  new Menu(menu);
</script>

각 버튼들에게 이벤트를 등록하지 않고도 원하는 작업을 실행할 수 있다. 이는 훨씬 간결한 코드를 작성할 수 있게 해준다. 또한 버튼이 추가로 생성/삭제되어도 보다 유연하게 이에 대처할 수 있게 된다.

 

참고


https://developer.mozilla.org/ko/docs/Web/API/EventTarget/addEventListener

https://ko.javascript.info/event-delegation

반응형

'JavaScript' 카테고리의 다른 글

[Javascript] Event Loop  (0) 2022.04.24
[Javascript] 깊은 복사(deep copy)  (0) 2022.03.27
웹팩(webpack) 알러지 치료하기 - 1  (0) 2022.02.27
[javaScript] export와 export default의 차이점  (0) 2021.11.30
[Javascript] Async, Await  (0) 2021.11.01