[JS] 이벤트, 위임(캡처링, 버블링)과 전파, 커스텀 이벤트

Jay ·2022년 10월 4일
0
post-custom-banner

1. Event ?

사건, 웹 브라우저가 알려주는 HTML 요소에 대한 사건의 발생을 의미

1) Event-driven programming

프로그램의 흐름을 이벤트 중심으로 제어하는 프로그래밍

이벤트와 그에 대응하는 함수(이벤트 핸들러)를 통해 사용자와 애플리케이션은 상호작용함.

2) Event type

200가지가 넘는 이벤트 타입이 존재.(MDN - Event reference)

마우스, 키보드, 포커스, 폼, 값 변경, DOM 뮤테이션, 뷰, 리소스 이벤트 총 8가지로 분류된다.

3) Event handler 등록

- Attribute vs Property

Attribute는 HTML에서 정의되는 속성이고, Property는 DOM에서 정의되는 속성

- Event handler - attribute 방식

HTML 어트리뷰트 값에 Statement(함수 호출문 등)를 할당.
(함수가 아닌 statement)

~~
<body>
  <button onclick="sayHi('Lee')">Click me!</button>
  <script>
    function sayHi(name){
      console.log(`Hi! ${name}.`);
  }
  </script>
</body>
// -> function onclick(event) { sayHi('Lee') }; 생성
// -> 인수를 넘기기 위함
~~
   
<button onclick="console.log("Hi!"); console.log('Lee')'">Click me</button>
//이렇게 여러개의 문을 할당할수도 있음.

이벤트 핸들러 어트리뷰트 방식은 가능은 하지만 지양해야함. HTML과 자바스크립트의 관심사는 다르므로 분리해야하기 때문.

하지만 모던 자바스크립트 프레임워크/라이브러리에서 이벤트 핸들러 어트리뷰트 방식으로 이벤트를 처리하기는 함.
CBD(Component base development)에서는 html,css, javascript를 같은 관심사 즉, 뷰를 구성하기 위한 구성요소로 보기 때문에 예외는 아님.

- Event handler - property 방식

DOM 노드 객체의 event handler property에 할당.
HTML과 자바스크립트가 뒤섞이는걸 방지할 수 있음.
하지만 이벤트 핸들러 프로퍼티에 하나의 이벤트 핸들러만 바인딩할 수 있음.

~~
<body>
  <button>Click me!</button>
  <script>
    $button.onclick = function () {
    console.log('button click');
  };
  </script>
</body>

//

- addEventListener 메서드 방식

DOM Level 2에서 도입.
앞선 두 방식은 DOM Level 0부터 존재.

addEventListener(type, listener, useCapture)

type 이벤트 타입 지정, listener는 이벤트 핸들러 지정.

useCaputure로 이벤트 전파 단계를 지정할 수 있다.
생략하거나 false를 지정하면 버블링 단계에서 이벤트를 캐치하고, true를 지정하면 캡처링 단계에서 이벤트를 캐치함.

4) 이벤트 핸들러 제거

addEventListner 메서드 방식을 사용했다면
removeEventListener 메서드로 제거. 방식 동일.

event handler - property 방식으로 등록했다면 해당 프로퍼티에 직접 null을 할당해줘야 함.

5) 이벤트 객체

이벤트가 발생하면 그에 관한 다양한 정보를 담고 있는 이벤트 객체가 동적으로 생성됨.
생성된 이벤트 객체는 이벤트 핸들러의 첫 번째 인수로 전달된다.

단, 이벤트 핸들러 attribute 방식의 경우, 이벤트 객체를 전달받으려면 이벤트 핸들러의 첫 번쨰 매개변수 이름이 반드시 event이어야 한다.

~~
<body onclick="showSomething(event)">
<script>
  const $msg = document.querySelector('.message');

  function showSomething(e) {
      $msg.textContent = 'clientX: ${e.clientX}, clientY: ${e.clientY}';
}
</script>
</body>
~~

//생성된 함수의 형태
function onclick(event) {
  showSomething(event)
}
// attrubyte속성이 생성된 함수의 몸체에 해당됨.

- 이벤트 객체의 상속 구조

이벤트가 발생하면 이벤트 타입에 따라 다양한 타입의 객체가 생성됨.
상속구조는 다음과 같다.

- 이벤트 객체의 공통 프로퍼티

Event 인터페이스에는 모든 이벤트 객체의 공통 프로퍼티가 정의되어 있음.

2. Event Propagation(전파)

Javascript Info - 버블링과 캡처링

DOM 트리 상의 DOM 요소 노드에서 발생한 이벤트는 DOM트리를 통해 전파된다.(이벤트 전파).

이벤트 전파가 일어나는 이유

이벤트 전파는 3단계로 구분할 수 있다.
Capturing phase - target phase - bubbling phase

1. 버블링 전파

버블링 전파 test

이벤트 객체가 target 에서 window방향으로 전파됨.

하지만, 예외적으로

  • 포커스 이벤트
  • 리소스 이벤트
  • 마우스 이벤트

는 버블링을 통해 전파되지 않는다.

이러한 경우에 캡처링 단계에서 이벤트를 캐치한다.
기본적으로 대부분의 경우 버블링 단계에서 캐치함.

버블링 중단은 event.stopPropagation()으로 가능합니다. 하지만, 이벤트 버블링을 막아야 하는 경우는 거의 없습니다. 버블링을 막아야 해결되는 문제라면 커스텀 이벤트 등을 사용해 문제를 해결할 수 있습니다.

2. 캡쳐링 전파

window에서 시작하여 이벤트 타깃방향으로 이벤트 객체가 전파됨.
캡쳐링 전파 test

또한 이벤트 핸들러 attribute, property 방식으로 등록한 이벤트 핸들러는 타겟 단계, 버블링 단계의 이벤트만 캐치가능하지만, addEventListener 메서드 방식으로 등록한 이벤트 핸들러는 캡쳐링 단계까지 캐치할 수 있음.

3. Event Delegation(위임)

여러 개의 하위 DOM 요소에 각각 이벤트 핸들러를 등록하는 대신 하나의 상위 DOM 요소에 이벤트 핸들러를 등록하는 것.

이벤트 위임을 하면 여러 개의 하위 DOM 요소에 일일이 이벤트 핸들러를 등록할 필요가 없음.

<!DOCTYPE html>
<html>
<body>
  <ul class="post-list">
    <li id="post-1">Item 1</li>
    <li id="post-2">Item 2</li>
    <li id="post-3">Item 3</li>
    <li id="post-4">Item 4</li>
  </ul>
  <script>
    const list = document.querySelector('.post-list')

    list.addEventListener('click', function (e) {
      console.log(e.target.id);
    });
  </script>
</body>
</html>

4. Custom Event

커스텀 이벤트 디스패치
click, mouseover, submit, scroll 등의 빌트인 이벤트는 DOM element에서만 작동함.
하지만 Javascript만을 사용해 이벤트를 직접 생성할 수 있다.
(커스텀 이벤트)

Custom Event 생성방법

- Event 생성자 사용 (old)

<h1 id="elem">Hello from the script!</h1>

<script>
  // 버블링이 일어나면서 document에서 이벤트가 처리됨
  document.addEventListener("hello", function(event) { // (1)
    alert("Hello from " + event.target.tagName); // Hello from H1
  });

  // 이벤트(hello)를 만들고 elem에서 이벤트 디스패치(실행)
  let event = new Event("hello", {bubbles: true}); // (2)
  elem.dispatchEvent(event);

  // document에 할당된 핸들러가 동작하고 메시지가 얼럿창에 출력됩니다.
</script>

- CustomEvent 생성자 사용

제대로 된 커스텀 이벤트를 만들려면 new CustomEvent를 사용해야 한다. CustomEvent는 Event와 거의 유사하지만 한 가지 다른 점이 있음.

CustomEvent의 두 번째 인수엔 객체가 들어갈 수 있는데, 개발자는 이 객체에 detail이라는 프로퍼티를 추가해 커스텀 이벤트 관련 정보를 명시하고, 정보를 이벤트에 전달할 수 있다.

<h1 id="elem">이보라님, 환영합니다!</h1>

<script>
  // 추가 정보는 이벤트와 함께 핸들러에 전달됩니다.
  elem.addEventListener("hello", function(event) {
    alert(event.detail.name);
  });

  elem.dispatchEvent(new CustomEvent("hello", {
    detail: { name: "보라" }
  }));
</script>

ETC

Q1. 리액트에서의 이벤트 처리가 바닐라 자바스크립트에서의 이벤트 처리와 다른점?

  • 리액트의 event는 소문자대신 카멜케이스 사용.
    onclick -> onClick

  • JSX를 사용하여 문자열이 아닌(string, ???) 함수로 이벤트 핸들러를 전달

  • DOM 엘리먼트의 경우 이벤트 핸들러가 false를 반환해도 해당 엘리먼트의 기본 동작을 방지할 수 있었지만, 리액트 엘리먼트의 경우 반드시 핸들러가 preventDefault를 호출해야 한다.

  • React가 지원하지 않는 DOM 이벤트 (ex: resize) -> addEventListener 사용해야함.

  • preventDefault를 명시적으로 직접 호출해야함.(js - return false)

Q2. Synthetic Event란?

synthetic event (합성함수) 는 브라우저의 네이티브 이벤트를 위한 크로스 브라우저 래퍼다. 이 api는 브라우저의 네이티브 이벤트와 동일하며, 마찬가지로 stopPropagation() preventDefault()도 포함하고 있지만, 모든 브라우저에서 동일하게 작동한다는 점이 다르다.

profile
Jay입니다.
post-custom-banner

0개의 댓글