FE/JavaScript 팔아요

[Note_deepDive]40장 이벤트

bomsbro 2022. 9. 28. 10:32

40.1 이벤트 드리븐 프로그래밍

- 이벤트는 브라우저가 특정 사건들을 감지하여 발생시킨다. 

- 이벤트가 발생했을 때 동작하는 함수르 이벤트 핸들러라고 한다. 호출 권한을 브라우저에게 위임하는 것이다.

- 이벤트 중심으로 프로그래밍하는 것을 이벤트 드리븐 프로그래밍이라고 한다.  

40.2 주요 이벤트 타입 8가지

1. 마우스 이벤트

 - click, dbclick, mousedown, mouseup, mousemove, mouseenter, mouseover, moseleave, mouseout  등... 오른쪽 왼쪽은 e.which와 e.button으로 판단한다. 브라우저 마다 다르다.

 - firefox, Safari, Chrome 그리고 Opera 계열의 브라우저는 which가 3일 때 오른쪽 버튼이다. 그리고 IE, Opera 계열의 경우, button이 2일때 오른쪽 버튼이다.

 

2. 키보드 이벤트

 - keydown, keypress, keyup

3. 포커스 이벤트

 - focus blur focusin focusout

4. 폼 이벤트

 - submit reset 

5. 값 변경 이벤트

 - input change readystatechange(http랑 다름. document.readState의 loading, interactive, complete를 의미)

6. DOM 뮤테이션 이벤트

 - DOMContentLoaded(html문서의 로드와 파싱이 완료되고 DOM생성이 완료되었을 때)

7. 뷰 이벤트

 - resize scroll

8. 리소스 이벤트(이미지, 폰트 등..)

 - laod unload abort error

40.3 이벤트 핸들러 등록의 3가지 방식

1. 이벤트 핸들러 어트리뷰트 방식

 - 이벤트에 on을 붙이고 html element에 attribute로 준다. 함소 참조문이 아닌 호출문을 할당한다.()가 붙어야 한다는 것이다. 참조문을 할당하면 결과가 이벤트 핸들러로 등록된다. 값을 반환하는 호출문을 핸들러로 등록하면, 이벤트 핸들러를 호출할 수 없다. 이벤트 핸들러에게 인수를 전달하기 위해서이다..?

 - ' '를 구분자로 넣어 여러 핸들러 등록도 가능하다. 

 - <button onClick="sayHi('hi')"/>

 - componet based development인 리액트에서는 어트리뷰트 방식을 사용한다. 

 

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

const $button = document.querySelector('button');

$button.onclick = function () {
  console.log("clicked");
};

 - 이런식으로 dom 객체를 가져와서 onclick 프로퍼티에 바인딩 한다.

 - 이벤트 등록을 html문에 바로 작성을 안한다는 점에서 코드가 깔끔해질수는 있지만, 핸들러 프로퍼티에 하나의 핸들러만 등록 가능하다. 

 

3.  addEventListener 방식

$button.addEventListener('click', function () {
  console.log('clicked');
});

 - DOM level 2부터 도입된 방식이다.

 - 이벤트 리스너 방식은 프로퍼티 방식과는 달리 on~ 이벤트 프로퍼티에 1대 1로 바인딩 되는게 아니다. 그래서 하나의 이벤트에 여러개 핸들러를 등록할 수있다. 등록된 순서대로 호출된다.

 - 3번째 인수는 이벤트 캡처링 설정에 사용되고 false를하면 버블링을하고 true를 하면 캡처링한다. 버블링이 기본 값이다.

40.4 이벤트 핸들러 제거

 - removeEventListener 메서드를 통해 addEventListener를 사용해 등록한 이벤트를 제거할 수 있다. 

 - addEventListener를 쓰면서 준 파라미터들을 완벽하게 동일하게 줘야 제거해준다. 두번째 인자가 같은 핸들러함수를 참조하고 있어야 가능하다. 같은 이름이어도 제거 가능하다.

 - 무기명함수의 경우는 제거할 수 없다. 

 - arcuments.callee도 사용가능한데 장려하지 않는다.

** 프로퍼티 방식은 removeEventHander로 제거할 수 없다. $button.onclick = null;처럼 해당 핸들로 프로퍼티에 null을 할당하면 제거된다.)

40.5 이벤트 객체

 (e) => {} : 해당되는 이벤트에 대한 다양한 정보를 가지고 있는 객체이다. 핸들러 콜백 함수의 인자로 받아서 쓸 수 있다. 리액트는 어트리뷰트 방식이어도 e는 event  등 아무이름이나 써도 된다. 그런데 일반적인 어트리뷰트 방식은 반드시 event라는 네이밍을 써야 한다. onClick="handleClick(event)"에서 스트링을 파싱한 후 암묵적으로 첫번째 인자 이름이 event인 해당 함수를 생성해서  onClick 이벤트 핸들러 프로퍼티에 할당되기 때문이다.

1. 이벤트 객체의 상속구조

 - Event는 Object를 상속받는다.

 - UIEvent는 Event를 상속받는다.

 - 앞에 나왔던 Mouse, Keyboard Event들은 UI Event를 상속받는다.

 - 해당 객체들은 생성자 함수다. New KeyboardEvent();로 생성할 수도 있다.

 - 이런 애들이 이벤트 발생할 때 암묵적으로 생성되어 핸들러 함수로 넘어오는 것이다.

 - load onChange의 이벤트 객체는 Event이다.

 - focus는 FocuseEvent 객체다.

 - keyup은 KeyboardEvent 객체다.

이런 식으로 이벤트에 따라 이벤트 타입이 달라진다.

2. 이벤트 객체의 공통 프로퍼티 9가지는 꼭 알아야 한다.

 - type, target, currentTarget, eventPhase, bubbles, cancelable, defaultPrevented, isTrusted, timeStamp

3. 마우스 정보 취득

 - 마우스 이벤트는 좌표정보, 버튼정보를 가지고 있을 수 있다. 드래그의 경우 마우스 다운에서 값을기록하고 업에서 값을 기록한 후 계산하면 이동거리를 계산할 수 있다. mosedown이 발생한 후 mouseup이 발생하지 않으면 누르고 있는 상태로 인식할 수 있다. 

 - screenX screenY clientX clientY pageX pageY offsetX offsetY에서 좌표값들을 받아올 수 있다. client는 뷰포트 기준의 좌표값을 받아온다.

 

4. 키보드 정보 취득

 - keyboardEvent타입의 객체는 altkey ctrlkey shiftkey metakey key, kecode 등의 고유 프로퍼티를 갖는다. 엔터키는 key프로퍼티 값이 'Enter'인지 아닌지로 판단할 수 있다.

https://keycode.info 에서 키값을 확인할 수 있다.

 - input 엘리먼트에 한글을 입력하고 엔터키를 누르면 keyup핸들러가 두번 호출된다. 그래서 keyup을 사용하기보다는 keydown이벤트를 사용해야 한다.

 

40.6 이벤트 전파

- 이벤트에는 캡처링 -> 타깃 -> 버블링 3단계로 진행된다.
- 처음에는 window에서 타깃까지 전파된다. 그리고 타겟에 도달하고, 타겟에서 window까지 전파된다.

 

** 어트리뷰트/프로퍼티 방식으로 등록한 이벤트 핸들러는 타깃단계와 버블링 단계의 이벤트만 캐치할 수 있다! 캡처링 단계의 이벤트를 캐치하려면 addEventListener의 3번째 인수에 true를 전달한다. 즉 리액트에서는 버블링 단계의 이벤트 핸들링만 가능하다..?**

 

** 포커스 이벤트 중 focus blur, 리소스 이벤트 중  load unload abort error, 마우스 이벤트 중 mouseenter mouseleave는 버블링 되지 않는다..! 그래서 상위 요소에서 캡처하려면 캡처링 단계의 이벤트를 잡아야한다. 캡처링이 안되면 해당 이벤트들을 대체한다. focusin/focusout mouseover/mouseout이 버블링을하는 이벤트다.

 

40.7 이벤트 위임

 - dom 요소에서 상위 요소에 이벤트 핸들러를 달아서 자식들에게 각각 이벤트 핸들러를 달아줘야 하는 문제를 해결하는 방식을 말한다.

 - 이 방식에서 주의해야할 점은 이벤트를 발생한 자식이 내가 의도한 애가 아닐 수도 있다는 것이다. 그래서 이벤트의 타겟을 검사해야 한다. target.matches() 메서드를 활용한다. target은 이벤트를 발생시킨 엘리먼트를 말하고 currentTarget은 내가 지금 핸들러를 달고 있는 바로 그 엘리먼트를 말한다.

40.8 DOM 요소의 기본 동작 조작

1. DOM요소의 기본 동작 중단

- 각 element는 저마다 고유한 동작이 있다. preventDefault는 이러한 기본 동작을 중단시킨다.

- a태그 이벤트 안에 e.preventDefault();를 넣으면 클릭시 링크로 이동하지 않는다.

 

2. 이벤트 전파 방지

- stopPropagation으로 엘리먼트에서 중간에 끊을 수 있다. 이벤트 핸들러에서 e.stopPropagation();을 사용한다. 부모자식이 둘다 onclick 프로퍼티에 각가 다르게 동작하는 핸들러가 있는 경우, 자식을 클릭했을 때 부모가 click event listen을 하지 않게 하려면 해당 메서드를 사용한다.

 

40.9 이벤트 핸들러함수 안의 this

1. 이벤트 핸들러 어트리뷰트 방식 안의 this

- 핸들러 함수 내부의 this는 window를 가리킨다. 핸들함수는 암묵적으로 이벤트 핸들러에 의해 일반함수로 호출된다. 일반함수 내부의 this는 window를 가리킨다. 하지만 핸들러가 this를 인자로 받았을 경우에는 바인딩한 dom요소를 가리킨다.

2. 프로퍼티 방식과 addEventListener 방식 안의 this

 - 둘 다 바인딩한 해당 dom요소를 가리킨다. 즉 e.currentTarget과 같다.

 

** 그런데 화살표 함수로 정의한 this는 단지 상위 스코프의 this를 가리킨다. 동적바인딩을 하지 않기 때문이다. **

** 클래스 메서드를 이벤트 핸들러로 쓸경우의 this를 주의해야하는데 이때 this는 클래스가 생성하는 인스턴스를 가리키지않고 바안딩한 dom요소를 가리킨다. 그래서, 멤버변수를 쓰려면 bind메서드로 this를 바인딩해줘야한다.

 **또는 핸들러 메서드를 화살표함수로 쓰고 this를 쓰면 멤버변수를 가리킨다. **

40.10 이벤트 핸들러에 인수 전달

- 이벤트 핸들러에 인수를 전달하려면 호출 시 전달한다. 어트리뷰트 방식은 호출문을 사용해서 인수를 전달할 수 있지만, 프로퍼티 방식과 addEventListener는 핸들러를 브라우저가 호출하기 때문에, 인수를 전달할 수 없다. 하지만, 핸들러 내부에서 인수를 가지는 함수를 호출하고 외부의 변수를 가져다가 쓰면 꼼수로 인수를 전달할 수 있다. 또는 핸들러를 리턴하는 함수로 래핑해서 인수를 전달할 수도 있다.

40.11 커스텀 이벤트

1. 커스텀 이벤트 생성

- new CustomEvent();로 생성할 수 있다. 인자로 이벤트의 type값 ex keyup 같은 것을 줄 수 있다.

- 두번째 인자로 고유 프로퍼티 객체를 전달한다.

- type, bubbles, cancelable 등을 이벤트 공통 프로퍼티를 똑같이 가진다.

**custom event 객체의 isTrusted는 항상 false이다. 사용자 행위에의해 발생한 이벤트 객체의 isTrusted는 항상 true다.**

 

2. 커스텀 이벤트 디스패치

 - 커스텀 이벤트는 dom의 dispatchEvent의 인자로 해당 커스텀 이벤트를 주면서 발생시킨다. 

 - e.detail로 이벤트 객체 생성할때 두번째 인자로 줬던 객체를 까볼 수 있다.

 - 임의의 이벤트 타입은 addEventListner를 사용해야한다.

 

궁금한점

 - dom의 레벨이란 무엇인가? 0 1 2 ... 이 있다는데? dom의 버전을 말하는 듯 하다.

 

참고자료 

 - 리액트에서의 이벤트 전파: https://www.robinwieruch.de/react-event-bubbling-capturing/