[JS] 40. 이벤트

김지민·2022년 6월 3일
0

자바스크립트

목록 보기
14/20
post-thumbnail

1. 이벤트 드리븐 프로그래밍

이벤트 핸들러: 이벤트가 일어났을 때 호출하고 싶은 함수
이벤트 핸들러 등록: 브라우저에게 호출하고 싶은 함수(이벤트 핸들러) 호출을 위임하는 것

이벤트가 발생했는지 개발자는 모르기 때문에 브라우저에게 함수를 위임한다.

이벤트 드리븐 프로그래밍: 프로그램의 흐름을 이벤트 중심으로 제어한느 프로그래밍 방식

2. 이벤트 타입

마우스 이벤트, 키보드 이벤트, 포커스 이벤트, 폼 이벤트
값 변경 이벤트, DOM 뮤테이션 이벤트, 뷰 이벤트, 리소스 이벤트

3. 이벤트 핸들러 등록

브라우저에게 함수를 위임하는 방법

3.1 이벤트 핸들러 어트리뷰트 방식

이벤트 핸들러 어트리뷰트: 접두사(on) + 이벤트의 종류

이벤트 핸들러 어트리뷰트 값으로 함수 호출문 등의 문을 할당하면 이벤트 핸들러가 등록된다.

<button onclick="sayHi('Lee')">Click me!</button>

/* 버튼의 onclick 이벤트 핸들러 어트리 뷰트에 등록 */

함수 참조가 아닌 함수 호출문 등의 문을 할당

이벤트 핸들러 어트리뷰트 값으로 함수 참조가 아닌 함수 호출문 등의 문을 등록해야한다.

만약 값을 반환하지 않는 함수 호출문을 할당했다면, 이벤트 핸들러 어트리트 값은 암묵적으로 이벤트 핸들러 어트리뷰트 이름과 동일한 키 onclick 이벤트 핸들러 프로퍼티에 할당한다.

반환 값이 없는 함수 호출문으로 호출을 위임하면 어트리뷰트 값에 이벤트 핸들러를 저장할 수 없다.

때문에 암묵적으로 이벤트 핸들러 어트리뷰트와 동일한 이름과 동일한 키 이벤트 핸들러를 함수 몸체를 생성하여 이벤트 어트리뷰트에 할당한다.

이벤트 어트리뷰트 값으로 함수 참조를 할당하면 이벤트 핸들러에 인수를 전달하기 곤란하다.

함수 참조가 아닌 함수 호출문을 이벤트 어트리뷰트에 할당한다
왜냐하면 이벤트 참조는 인수를 전달할 수 없기 때문이다

3.2 이벤트 핸들러 프로퍼티 방식

DOM 노드 객체는 이벤트에 대응하는 이벤트 핸들러 프로퍼티를 가지고 있다.

  $button.onclick = function () {
      console.log('button click');

버튼 요소 이벤트 핸들러 프로퍼티에 이벤트 핸들러를 바인딩

이벤트 타깃
이벤트 타입
이벤트 핸들러

"이벤트 핸들러 프로퍼티 방식"은 "이벤트 핸들러 어트리뷰트 방식"의 HTML과 자바스크립트가 뒤섞이는 문제를 해결할 수 있다.
하지만 이벤트 핸들러 프로퍼티에 하나의 이벤트 핸들러만 바인딩 할 수 있는 단점이 있다.

3.3 addEventListener 메서드 방식

이벤트 타킷.addEventListener('이벤트 타입', 이벤트 핸들러);

addEventListener 메서드의 첫번째 매개변수에는 이벤트의 종류를 나타내는 문자열인 이벤트 타입을 전달한다.
마지막 매개변수에는 이벤트를 캐치할 이벤트 전파 단계를 지정한다.

addEventListener 메서드는 하나 이상의 이벤트 핸들러를 등록할 수 있다. 이때, 이벤트 핸들러는 등록된 순서대로 호출된다.

참조가 동일한 이베트 핸들러를 중복 등록하면 하나의 이벤트 핸들러만 등록한다.

4. 이벤트 핸들러 제거

addEventListener 메서드로 등록한 이벤트 핸들러를 제거하려면, EventTarget, prototype, removeEventListener 메서들르 사용한다.

addEventListner 메서드에 전달한 인수와 removeListener 메서드에 전달한 인수가 일치하지 않으면 이벤트 핸들러가 제거되지 않는다.

무명 함수를 이벤트 핸들러로 등록한 경우 제거할 수 없다.

기명 함수를 이벤트 핸들러로 등록할 수 없다면

이벤트 핸들러 프로퍼티 방식으로 등록한 이벤트 핸들러 removeEventListener 메서드로 제거할 수 없다.
이벤트 핸들러 프로퍼티 방식으로 등록한 이벤트 핸들러를 제거하려면 이벤트 핸들러 프로퍼티에 null을 할당한다.

5. 이벤트 객체

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

클릭 이벤트에 의해 생성된 이벤트 객체는 이벤트 핸들러의 첫번째 인수로 전달된어 매개변수 e에 암묵적으로 할당된다.

이는 브라우저가 이벤트 핸들러를 호출할 때 이벤트 객체를 인수로 전달하기 때문이다.

이벤트 객체를 전달받으려면 이벤트 핸들러를 정의할 때, 이벤트 객체를 전달받을 매개변수를 명시적으로 선언해야 한다.

이벤트 핸들러는 이벤트 객체를 전달 받을 매개변수를 명시적으로 선언해야한다.

이벤트 핸들러 어트리뷰트 방식으로 이벤트 객체를 전달받으려면 이벤트 핸들러의 첫 번째 매개변수 이름이 반드시 evetn이어야 한다. 만약 event가 아닌 다른 이름으로 매개변수를 선언하면 이벤트 객체를 전달받지 못한다.

5.1 이벤트 객체의 상속 구조

Event, UIEvent, MouseEvent 등 모두는 생성자 함수이다.
생성자 함수를 호출하여 이벤트 객체를 생성할 수 있다.

**이벤트가 일어나지도 않았는데 이벤트 객체를 생성하는 이유는?

Event 인터페이스는 DOM 내에서 발생한 이벤트에 의해 생성된 이

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

이벤트 객체의 공통 프로퍼티는 type, target, currentTarget, eventPhase, bubbles, cancelabel, defaultPrevented, isTrusted, timeStamp 등이 있다.

5.3 마우스 정보 취득

click, dbclick, mousedown, mouseup, mousemove, mouseenter, mouseleave 이벤트가 발생하면 생성되는 MouseEvent 타입의 이벤트 객체는 다음과 같은 고유의 프로퍼티를 갖는다.

  • 마우스 포인터의 좌표 정보를 나타내는 프로퍼티: screenX/screenY/, clinetX/clientY, pageX/pageY, offsetX/offsetY
  • 버튼 정보를 나타내는 프로퍼티: altKey, ctrlKey, shiftKey, button

5.4 키보드 정보 취득

keydown, keyup, keypress 이벤트가 발생하면 생성되는 KeyboardEvent 타입의 이벤트 객체는 altKey, crtlKey, shiftKey, metaKey, key, keyCode 같은 고우의 프로퍼티를 갖는다.

6. 이벤트 전파

DOM 트리상에 존재하는 DOM 요소 노드에서 발생한 이벤트는 DOM 트리를 통해 전파된다. 이를 이벤트 전파라고 한다.


<!DOCTYPE html>
<html>
  <body>
    <ul id="fruits">
      <li id="apple">apple</li>
      <li id="banana">banana</li>
      <li id="orange">orange</li>
    </ul>
  </body>
</html>

ul 요소의 두번째 자식 요소인 li 요소를 클릭하면 이벤트가 발생한다. 이때 생성된 이벤트 객체는 이벤트를 발생시킨 DOM요소인 이벤트 타깃을 중심으로 DOM 트리를 통해 저파된다.
이벤트 전파는 이벤트 객체가 전파되는 방향에 따라 다음과 같이 3 단계로 구분할 수 있다.

  • 캡처링 단계 : 이벤트가 상위 요소에서 하위 요소 방향으로 전파
  • 타깃 단계: 이벤트가 이벤트 타깃에 도달
  • 버블링 단계: 이벤트가 하위 요소에 상위 요소 방향으로 전파

예를 들어, 다음 예제와 같이 ul 요소에 이벤트 핸들러를 바인당하고 ul 요소의 하위 요소인 li 요소를 클릭하여 이벤트를 발생시켜보자, 이벤트 타깃은 li 요소이고, 커런트 타깃은 ul 요소이다.

7. 이벤트 위임

사용자가 내비게이션 아이템을 클릭하여 선택하면 현재 선택된 내비게이션 아이템에 active 클래스를 추가하는 예제를 살펴보자

<!DOCTYPE html>
<html>
  <head>
    <style>
      #frutis {
      disply: flex;
      list-style-type: none;
      padding:0;
      }
      
      #fruits li {
      width: 100px;
      cursor: pointer;
      }
      
      #fruits.active {
      color: red;
      text-decoration: underline;
      }
    </style>
  </head>
  <body>
    <nav>
      <ul id="furits">
        <li id="apple" class="active"> Apple</li>
        <li id="banana">Banana</li>
        <li id="orange">Orange</li>
      </ul>
    </nav>
    <div>선택된 내비게이션 아이템: <em class="msg">apple></em></div>
    <script>
      const $fruits = documnet.getElementById('fruits');
      const $msg = document.querySelector('.msg');
      
      // 사용자 클릭에 의해 선택된 내비게이션 아이템에 active 클래스를 추가하고
      // 그 외의 모든 내비게이션 아이템의 active 클래스를 제거한다.
      function activate({target}) {
      [...$fruits.children].forEach($fruit => {
      $fruit.classList.toggle('active', $fruit === target);
      $msg.textContent = target.id;
      });
      }
      
      // 모든 내비게이션 아이템에 이벤트 핸들러를 등록한다.
      document.getElementById('apple').onclick = activate;
      document.getElementById('banana').onclick = activate;
      document.getElemnetById('orange').onclick = activate;
      
    </script>
  </body>
</html>

모든 내비게이션 아이템이 클릭 이벤트에 반응하도록 모든 내비게이션 아이템에 이벤트 핸들러인 activate를 등록했다.

이벤트 위임은 여러 개의 하위 DOM 요소에 각각 이벤트 핸들러를 등록하는 대신에 하나의 상위 DOM 요소에 이벤트 핸들러를 등록하는 방법을 말한다.

이벤트 위임을 통해 하위 DOM 요소에서 발생한 이벤트를 처리할 때 주의할 점은 상위 요소에 이벤트 핸들러를 등록하기 때문에 이벤트 타깃, 즉 이벤트를 실제로 발생시킨 DOM 요소가 개발자가 기대한 DOM 요소가 아닐 수 도 있다는 것이다.

Element.prototype.matches 메서드는 인수로 전달된 선택자에 의해 특정 노드를 탐색 가능하지 확인한다.

function activate({target}) {
  // 이벤트를 발생시킨 요소가 ul#frutis의 자식 요소가 아니라면 무시한다.
  if (!target.matches('#fruits>li')) return;

이벤트 객체의 currentTarget 프로퍼티는 언제나 변함없이 $fruits 요소를 가리키지만 이벤트 객체의 target 프로퍼티는 실제로 이벤트를 발생시킨 DOM 요소를 가리킨다.

$fruits 요소도 클릭 이벤트를 발생시킬 수 있으므로 이 경우 이벤트 객체의 currentTarget 프로퍼티와 target 프로퍼티는 동일한 $fruit 요소를 가리키지만 $fruit 요소의 하위 요소에서 클릭 이벤트가 발생한 경우 이벤트 객체의 currentTarget 프로퍼티와 target 프로퍼티는 다른 DOM 요소를 가리킨다.

8. DOM 요소의 기본 동작 조작

8.1 DOM 요소의 기본 동작 중단

preventDefault 메서드는 이러한 DOM 요소의 기본 동작을 중단시킨다.

8.2 이벤트 전파 방지

stopPropagation 메서드는 이벤트 전파를 중지시킨다.

상위 DOM 요소인 container 요소에 이벤트를 위임했다.
따라서 하위 DOM 요소에서 발생한 클릭 이벤트를 상위 DOM 요소인 container 요소가 캐치하여 이벤트를 처리한다.

하지만 하위 요소 중에서 btn2요소는 자체적으로 이벤트를 처리한다. 이때 btn2 요소는 자신이 발생시킨 이벤트가 전파되는 것을 중단하여 자신에게 바인딩된 이벤트 핸들러만 실행되도록 한다.

이처럼 stopPropagation 메서드는 하위 DOM 요소의 이벤트를 개별적으로 처리하기 위해 이벤트 전파를 중단시킨다.

9. 이벤트 핸들러 내부의 this

이벤트 핸들러 또한 함수 형태이고 this를 가지고 있다.
대부분의 this는 함수 호출 방법에 의해서 결정된다.

9.1 이벤트 핸들러 어트리뷰트 방식

이벤트 핸들러 어트리뷰트의 값은 암묵적으로 생성된 이벤트 핸들러의 문이다.
따라서 함수 내부의 this는 전역 객체 window를 가리킨다.

이벤트 핸들러를 호출할 때 인수로 전달한 this는 이벤트를 바인딩한 DOM 요소를 가리킨다.

위 예제에서 handleClick함수에 전달한 this는 암묵적으로 생성된 이벤트 핸들러 내부의 this다.
즉, 이벤트 핸들러 어트리뷰트 방식에 의해 암묵적으로 생성된 이벤트 핸들러 내부의 this는 이벤트를 바인딩한 DOM 요소를 가리킨다.

이는 이벤트 핸들러 프로퍼티 방식과 동일하다.

9.2 이벤트 핸들러 프로퍼티 방식과 addEventListener 메서드 방식

이벤트 핸들러 프로퍼티 방식과 addEventListener 메서드 방식 모두 이벤트 핸들러 내부의 this는 이벤트를 바인딩한 DOM 요소를 가리킨다. 즉, 이벤트 핸들러 내부의 this는 이벤트 객체의 currentTarget 프로퍼티와 같다.

  • 화살표 함수로 정의한 이벤트 핸들러 내부의 this는 상위 컨텍스트의 this를 가리킨다.
    화살표 함수는 함수 자체의 this 바인딩을 갖지 않는다.

  • 클래스에서 이벤트 핸들러를 바인딩한 경우 this에 주의해야 한다. 다음 예제에서는 이벤트 핸들러 프로퍼티 방식을 사용하고 있으나 addEventListener 메서드 방식을 사용하는 경우와 동일한다.

increase 메서드를 이벤트 핸들러로 등록

this.$button.onclick = this.increase;

increase() {
// 이벤트 핸들러 increase 내부의 this는 DOM 요소를 가리킨다.
//따라서 this.buttonthis.button은 this.button은 this.button.button.button과 같다.

위 예제의 increase 메서드 내부의 this는 클래스가 생성할 인스턴스를 가리키지 않는다.
이벤트 핸들러 내부의 this는 이벤트 바이닝한 DOM 요소를 가리키기 때문이다.
increase 메서드 내부의 this는 this.$button을 가리킨다. 따라서 increase 메서드를 이벤트 핸들러로 바인딩할 때 bind 메서드를 사용해 this를 전달하여 increase 메서드 내부의 this가 클래스가 생성할 인스턴스를 가리키도록 해야한다.

bind() 메소드가 호출되면 새로운 함수를 생성한다.
받게 되는 첫 인자의 value로는 this 키워드를 설정하고, 이어지는 인자들은 바인드된 함수의 인수를 제공합니다.

또는 클래스 필드에 할당한 화살표 함수를 이벤트 핸들러로 등록하여 이벤트 핸들러 내부의 this가 인스턴스를 가리키도록 할 수도 있다. 다만 이때 이벤트 핸들러 increase는 프로토타입 메서드가 아닌 인스턴스 메서드가 된다.

class의 이벤트 핸들러 내부의 this는 이벤트 바인딩한 DOM 요소를 가리킨다.
클래스가 생성할 인스턴스를 가리키기 위해서는 bind 혹은 화살표 함수를 사용해야한다

10. 이벤트 핸들러에 인수 전달

이벤트 핸들러 내부에서 함수를 호출하면서 인수를 전달할 수 있다.

이벤트 핸들러 내부에서 함수를 호출하면서 인수를 전달할다.
이벤트 핸들러를 반환하는 함수를 호출하면서 인수로 전달한다.

11. 커스텀 이벤트

11.1 커스텀 이벤트 생성

이벤트가 발생하면 암묵적으로 생성되는 이벤트 객체는 발생한 이벤트의 종류에 따라 이벤트 타입이 결정된다.

하지만, Event, UIEvent, MouseEvent와 같은 이벤트 생성자 함수를 호출하여 명시적으로 생성한 이벤트 객체는 임의의 이벤트 타입을 지정할 수도 있다.

11.2 커스텀 이벤트 디스패치

profile
💡Habit is a second nature. [Git] https://github.com/Kimjimin97

0개의 댓글