모던 자바스크립트 Deep Dive 40장 정리 - 이벤트

Hyodduru ·2022년 9월 20일
0
post-thumbnail

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

  • 이벤트 핸들러(event handler) ? 이벤트가 발생했을 때 호출될 함수
  • 이벤트 핸들러 등록 ? 이벤트가 발생했을 때 브라우저에게 이벤트 핸들러의 호출을 위임하는 것.

함수를 언제 호출할지 알 수 없으므로 개발자가 명시적으로 "함수를 호출"하는 것이 아니라 "브라우저에게 함수 호출을 위임"하는 것.
ex) onclick event - 사용자가 언제 클릭할 지 알 수 없으므로 브라우저에게 특정함수(이벤트 핸들러)를 호출하도록 브라우저에게 위임(이벤트 핸들러 등록)할 수 있음.

이벤트 드리븐 프로그래밍(event-driven-programming)? 프로그램의 흐름을 이벤트 중심으로 제어하는 프로그래밍 방식

2. 이벤트 타입

이벤트 타입(event type) ? 이벤트의 종류를 나타내는 문자열
ex) click

사용 빈도가 높은 이벤트들 정리

마우스 이벤트

click,dblclick, mousedown, mouseup, mousemove, mouseenter, mouseover, mouseleave, mouseout 등

키보드 이벤트

keydown, keypress, keyup 등

포커스 이벤트

focus, blur, focusin, focusout
🔖 focus, blur는 버블링이 되지않음. focusin과 focusout은 버블링이 된다.
focusin, focusout 이벤트 핸들러를 이벤트 핸들러 프로퍼티 방식으로 등록하면 크롬, 사파리에서 정상 동작하지 않음. focusin, focusout 이벤트 핸들러는 addEventListener 메서드 방식을 사용해 등록해야 함.

폼이벤트

submit

값 변경 이벤트

input, change, readystatechange(HTML 문서의 로드와 파싱 상태를 나타내는 document.readyState 프로퍼티 값('loading', 'interactive', 'complete')이 변경될 때)

DOM 뮤테이션 이벤트

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

뷰 이벤트

resize(오직 window객체 에서만 발생, window 크기를 resize할 때 연속적으로 발생), scroll

리소스 이벤트

load(DOMContentLoaded 이벤트가 발생한 이후 모든 리소스의 로딩이 완료되었을 때), unload(주로 새 웹페이지 요청한 경우), abort(리소스 로딩이 중단되었을 때), error

3. 이벤트 핸들러 등록

함수 호출을 브라우저에게 위임하는 것

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

HTML 요소의 이벤트 어트리뷰트 중에는 이벤트에 대응하는 이벤트 핸들러 어트리뷰트가 있다. 이벤트 핸들러의 이름은 onclick과 같이 on접두사 + 이벤트 타입 으로 이루어져 있음.
이벤트 핸들러 어트리뷰트 값으로 함수 호출문 등의 문(statement)을 할당하면 이벤트 핸들러가 등록됨.

<button onClick="sayHi('Lee')">Click me!</button>
<script>
  function sayHi(name){
  	console.log(`Hi! ${name}`);
}
  </script>

🚨 주의할 점) 이벤트 핸들러의 어트리뷰트 값으로 함수 참조가 아닌 함수 호출문 등의 문을 할당한다는 것!

이벤트 핸들러 어트리뷰트 값은 사실 암묵적으로 생성될 이벤트 핸들러의 함수 몸체를 의미함.

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

// 이벤트 핸들러에 인수를 전달하기 곤란함. 
<button onclick="sayHi">Click me!</button>

하지만 사용하지 않는 것이 좋음! HTML과 자바스크립트는 관심사가 다르므로 혼재하는 것보다는 분리하는 것이 좋음.

하지만 모던 자바스크립트에서는 이벤트 핸들러 어트리뷰트 방식을 사용하는 경우가 있음. CBD(Component Based Development) 방식의 Angular/React/Svelte/Vue.js 같은 프레임워크/라이브러리에서는 이벤트 핸들러 어트리뷰트 방식으로 처리.

CBD에서는 HTML, CSS, 자바스크립트르 관심사가 다른 개별적인 요소가 아닌, 뷰를 구상하기 위한 구성 요소로 보기 때문에 관심사가 다르다고 생각하지 않음!

// Angular
<button (click)="handleClickc($event)">Save</button>

// React 
<button onClick={handleClick}>Save</button>

// Svelte 
<button on:click={handleClick}>Save</button>

// Vue.js
<button v-on:click="handleClick($event)">Save</button>

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

window 객체와 Document, HTMLElement 타입의 DOM 노드 객체는 이벤트에 대응하는 이벤트 핸들러 프로퍼티를 가지고 있음.
이벤트 핸들러 프로퍼티의 키는 이벤트 핸들러 어트리뷰트와 마찬가지로 onclick과 같이 on접두사와 이벤트 타입으로 이루어져있음!

<button>Click me!<.button>
<script>
  const button = document.querySelector('button');
 
// 이벤트 핸들러 프로퍼티에 이벤트 핸들러를 바인딩 
  button.onclick = function(){
    console.log('button click!');}
</script>

//button : event target 
// onclick : on + event type
// function~~ : event handler 

이벤트 핸들러를 등록하기 위해서는 이벤트를 발생시킬 객체인 이벤트 타깃(event target)과 이벤트 타입, 이벤트 핸들러를 지정할 필요가 있음!

이벤트 핸들러 프로퍼티 방식은 하나의 이벤트에 하나의 이벤트 핸들러만을 바인딩할 수 있음.
첫번째로 바인딩된 이벤트 핸들러는 두번째로 바인딩 된 핸들러에 의해 재할당되어 실행되지 않음!

addEventListener 메서드 방식

EventTarget.prototype.addEventListener('eventType', functionName, [, useCapture] 메서드

useCapture : captrue 사용 여부(true : capturing, false : bubbling 기본값)

addEventListener 메서드 방식은 이벤트 핸들러 프로퍼티에 바인딩된 이벤트 핸들러에 아무런 영향을 주지 않음.

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

addEventListener 메서드는 하나 이상의 이벤트 핸들러를 등록할 수 있음. 이 때 핸들러는 등록된 순서대로 호출된다! (이벤트 핸들러 프로퍼티 방식과의 차이)

단, 참조가 동일한 이벤트 핸들러를 중복 등록하면 하나의 핸들러만 등록된다!

4. 이벤트 핸들러 제거

EventTarget.prototype.removeEventListner 메서드를 사용한다.

removeEventListener 메서드에 인수로 전달한 이벤트 핸들러는 addEventListener 메서드에 인수로 전달한 등록 이벤트 핸들러와 동일한 함수이어야 함! 따라서 무명 함수를 이벤트 핸들러로 등록한 경우 제거할 수 없음.

단, 기명 이벤트 핸들러 내부에서 removeEventListener 메서드를 호출하여 이벤트 핸들러를 제거하는 것은 가능. 이때 이벤트 핸들러는 단 한 번만 호출된다.

button.addEventListener('click', function foo(){
  	console.log('button click');
  // 이벤트 핸들러를 제거한다. 따라서 이벤트 핸들러는 단 한 번만 호출된다. 
  button.removeEventListener('click', foo);
}

기명 함수를 이벤트로 등록할 수 없다면 호출된 함수, 즉 함수 자신을 가리키는 arguments.callee를 사용할 수도 있음!

button.addEventListener('click', function foo(){
  	console.log('button click');
  
  button.removeEventListener('click', arguments.callee);
}

🚨 arguments.callee는 코드 최적화를 방해하므로 strict mode에서 사용이 금지된다. 사용하지 않는 것이 좋음!

이벤트 핸들러 프로퍼티 방식으로 등록한 이벤트 핸들러는 removeEventListener 메서드로 제거할 수 없음.

button.onclick = handleClick;

// removeEventListener 메서드로 이벤트 핸들러를 제거할 수 없다. 
button.removeEventListener('click', handleClick);

// 이벤트 핸들러 프로퍼티에 null을 할당하여 이벤트 핸들러를 제거한다.
button.onclick = null; 

5. 이벤트 객체

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

// 이벤트 핸들러 어트리뷰트 방식의 경우 event가 다른 이름으로는 이벤트 객체를 전달받지 못한다. 
<body onclick="showCoords(event)">
  
<script>
	const msg = document.querySelector('.message');

// 클릭 이벤트에 의해 생성된 이벤트 객체는 이벤트 핸들러의 첫 번째 인수로 전달된다. 
function showCoords(e){
  	msg.textContext = `clientX : ${e.clientX}, clientY : ${e.clientY}`; };
</script>
  
  </body>
  

참고로, 이벤트 핸들러 어트리뷰트 방식으로 이벤트 핸들러를 등록했다면 event를 통해 이벤트 객체를 전달받을 수 있다.

즉 event가 아닌 다른 이름으로는 이벤트 객체를 전달받지 못한다!

그 이유는, 이벤트 핸들러 어트리뷰트 값은 사실 암묵적으로 생성되는 이벤트 핸들러의 함수 몸체를 의미하기 때문. 즉, 어트리뷰트는 파싱되어 다음과 같은 함수를 암묵적으로 생성하여 onclick 이벤트 핸들러 프로퍼티에 할당한다.

function onclick(event){
  showCoords(event);}

이벤트 객체의 상속 구조

Object - Event - (AnimationEvent, UIEvent 등등 ) - (FocusEvent, MouseEvent 등등) - (DragEvent, WheelEvent 등등)

위의 Event들은 모두 "생성자 함수"이다. 따라서 생성자 함수를 호출하여 이벤트 객체를 생성할 수 있음.


// Event 생성자 함수를 호출하여 foo 이벤트 타입의 Event 객체를 생성
let e = new Event('foo');
console.log(e);
// Event {isTrusted : false, type : 'foo', ...}

console.log(e instanceof Event); // true
console.log(e instanceof Object); // true 

이처럼 이벤트가 발생하면 암묵적으로 생성되는 이벤트 객체도 생성자 함수에 의해 생성된다. 그리고 생성된 이벤트 객체는 생성자 함수와 더불어 생성되는 프로토타입으로 구성된 프로토타입 체인의 일원이 된다.

✔️ Event 인터페이스는 DOM 내에서 발생한 이벤트에 의해 생성되는 이벤트 객체를 나타낸다. Event 인터페이스에는 모든 인터페이스 객체의 공통 프로퍼티가 정의되어 있다.
✔️ FocusEvent, MouseEvent, KeyboardEvent 와 같은 하위 인터페이스에는 이벤트 타입에 따라 고유한 프로퍼티가 정의되어 있다.

즉, 이벤트 객체의 프로퍼티는 발생한 이벤트 타입에 따라 달라짐!

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

Event 인터페이스, 즉 Event.prototype에 정의되어 있는 이벤트 관련 프로퍼티는 UIEvent, CustomEvent, MouseEvent 등 모든 파생 이벤트 객체에 상속된다. 즉, Event 인터페이스의 이벤트 관련 프로퍼티는 모든 이벤트 객체가 상속받는 공통 프로퍼티다.

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

  • type : 이벤트 타입
  • target : 이벤트를 발생시킨 DOM 요소
  • currentTarget : 이벤트 핸들러가 바인딩된 DOM 요소
  • eventPhase : 이벤트 전파단계 (0 : 이벤트 없음, 1:캡처링 단계, 2: 타깃 단계, 3:버블링단계)
  • bubbles : 이벤트를 버블링으로 전파하는지 여부.
  • cancelable : preventDefault 메서드를 호출하여 이벤트의 기본 동작을 취소할수 있는지 여부.
  • defaultPrevented : preventDefault 메서드를 호출하여 이벤트를 취소했는지 여부.
  • isTrusted : 사용자의 행위에 의해 발생한 이벤트인지 여부.
  • timestamp : 이벤트가 발생한 시각

마우스 정보 취득

mouseEvent 타입의 이벤트 객체가 갖는 고유의 프로퍼티들

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

키보드 정보 취득

KeyboardEvent 타입의 이벤트 객체가 갖는 고유의 프로퍼티들

altKey, ctrlKey, shiftKey, metaKey, key, keyCode 등

input.keyup = e => {
  if(e.key !== 'Enter') return;
};
profile
꾸준히 성장하기🦋 https://hyodduru.tistory.com/ 로 블로그 옮겼습니다

0개의 댓글