- Published on
2024.09.22
[딥다이브] - 40. 이벤트(1)
40.1. 이벤트 드리븐 프로그래밍
브라우저는 특정 사건이 발생하면 이벤트를 발생시킨다. 애플리케이션은 이벤트에 반응하여 호출될 함수를 브라우저에게 위임한다.
이벤트 핸들러: 호출될 함수
이벤트 핸들러 등록: 호출을 위임하는 것
이벤트 드리븐 프로그래밍: 프로그램의 흐름을 이벤트 중심으로 제어하는 방식
40.2. 이벤트 타입
40.2.1. 마우스 이벤트
이벤트 타입 | 이벤트 발생 시점 |
---|---|
click | 마우스를 클릭했을 때 |
dbclick | 더블 클릭했을 때 |
mousedown | 눌렀을 때 |
mouseup | 누르고 있던 버튼을 놓았을 때 |
mousemove | 커서를 움직였을 때 |
mouseenter | 커서를 HTML 요소 안으로 이동했을 때 (버블링 X) |
mouseover | 커서를 HTML 요소 안으로 이동했을 떄 (버블링 O) |
mouseleave | 커서를 HTML 요소 밖으로 이동했을 떄 (버블링 X) |
mouseout | 커서를 HTML 요소 밖으로 이동했을 떄 (버블링 O) |
40.2.2. 키보드 이벤트
이벤트 타입 | 이벤트 발생 시점 |
---|---|
keydown | 모든 키를 눌렀을 때 발생 |
keypress | 문자 키를 눌렀을 때 연속적으로 발생 (deprecated되었으므로 사용하지 않을 것) |
keyup | 누르고 있던 키를 놓았을 때 한 번만 발생 |
- keydown
control, option, shift, tab, delete, enter, 방향 키와 문자, 숫자, 특수 문자 키를 눌렀을 때 발생, 문자, 숫자, 특수 문자, enter 키 입력 시는 연속적, 그 외의 키는 한 번만 발생 - keypress
control, option, shift, tab, delete, 방향 키 등을 눌렀을 때는 발생하지 않고 문자, 숫자, 특수 문자, enter 키를 눌렀을 때만 발생. - keyup
keydown과 마찬가지로 control, option, shift, tab, delete, enter, 방향 키와 문자, 숫자, 특수 문자 키를 놓았을 때 발생
40.2.3. 포커스 이벤트
이벤트 타입 | 이벤트 발생 시점 |
---|---|
focus | HTML 요소가 포커스를 받았을 때 (버블링 X) |
blur | HTML 요소가 포커스를 잃었을 때 (버블링 X) |
focusin | HTML 요소가 포커스를 받았을 때 (버블링 O) |
focusout | HTML 요소가 포커스를 잃었을 때 (버블링 O) |
focusin, focusout 이벤트 핸들러를 이벤트 핸들러 프로퍼티 방식으로 등록하면 크롬, 사파리는 동작 X, addEventListener
방식으로 등록해야 함
40.2.4. 폼 이벤트
이벤트 타입 | 이벤트 발생 시점 |
---|---|
submit | 1. form 요소 내의 input(text, checkbox, radio), select 입력 필드(textarea 제외)에서 엔터키를 눌렀을 때 2. form 요소 내의 submit 버튼을 클릭했을 때 |
reset | form 요소 내의 reset 버튼을 클릭했을 때 (최근에는 사용 X) |
40.2.5. 값 변경 이벤트
이벤트 타입 | 이벤트 발생 시점 |
---|---|
input | input, select, textarea 요소의 값이 입력되었을 때 |
change | input, select, textarea 요소의 값이 변경되었을 때 |
readystatechange | HTML 문서의 로드와 파싱 상태를 나타내는 document.readyState 값('loading', 'interactive', 'complete')이 변경될 때 |
- change
change 이벤트는 input 이벤트와 달리 HTML 요소가 포커스를 잃었을 때 입력이 종료되었다고 인식하여 발생한다. 입력 중에는 input 이벤트가, 입력이 종료되어 값이 변경되면 change 이벤트가 발생한다.
40.2.6. DOM 뮤테이션 이벤트
이벤트 타입 | 이벤트 발생 시점 |
---|---|
DOMContentLoaded | HTML 문서의 로드와 파싱이 완료되어 DOM 생성이 완료되었을 때 |
40.2.7. 뷰 이벤트
이벤트 타입 | 이벤트 발생 시점 |
---|---|
resize | 브라우저 윈도우 크기를 리사이즈할 때 연속적으로 발생 (window 객체에서만 발생) |
scroll | 웹페이지 또는 HTML 요소를 스크롤할 때 연속적으로 발생 |
40.2.8. 리소스 이벤트
이벤트 타입 | 이벤트 발생 시점 |
---|---|
load | DOMContentLoaded 입네트가 발생한 후, 모든 리소스의 로딩이 완료되었을 때 |
unload | 리소스가 언로드될 때(주로 새 페이지를 요청한 경오) |
abort | 리소스 로딩이 중단되었을 때 |
error | 리소스 로딩이 실패했을 때 |
40.3. 이벤트 핸들러 등록
이벤트 핸들러의 등록 방법은 3가지다.
40.3.1. 이벤트 핸들러 어트리뷰트 방식
이벤트 핸들러 어트리뷰트의 이름은 on 접두사와 이벤트 타입으로 이루어져 있다. 주의점은 이벤트 핸들러 어트리뷰트 값으로 함수 참조가 아닌 함수 호출문 등의 문을 할당한다. 함수 호출을 위임하는 것이므로 콜백 함수와 마찬가지로 함수 참조를 등록해야 브라우저가 이벤트 핸들러를 호출할 수 있다. 함수 참조가 아니라 함수 호출문 등록 시 평가 결과가 이벤트 핸들러로 등록된다.
이벤트 핸들러 어트리뷰트 값은 암묵적으로 생성될 이벤트 핸들러의 함수 몸체를 의미한다. 이같은 이유는 이벤트 핸들러에 인수를 전달하기 위해서다.
function onclick(event) {
sayHi('Hi')
}
이벤트 핸들러 어트리뷰트 방식은 관심사가 서로 다른 HTML과 자바스크립트가 혼재해 분리하는 것이 좋지만, CBD 방식의 앵귤러, 리액트, 스벨트, 뷰와 같은 프레임워크/라이브러리는 이벤트 핸들러 어트리뷰트 방식으로 이벤트를 처리한다. CBD에서는 HTML, CSS, 자바스크립트를 관심사가 다른 개별요소라고 보지 않기 때문에 그렇다.
40.3.2. 이벤트 핸들러 프로퍼티 방식
이벤트 핸들러 프로퍼티의 키는 on 접두서와 이벤트 타입으로 이루어져 있다. 이벤트 핸들러를 등록하기 위해서는 이벤트 타깃
, 이벤트 타입
, 이벤트 핸들러
를 지정한다.
$button.onclick = function () {
console.log('button click')
}
이벤트 핸들러 프로퍼티 방식은 어트리뷰트 방식의 HTML과 자바스크립트가 혼재하는 문제를 해결할 수 있다.
40.3.3. addEventListener 메서드 방식
DOM Level 2에서 도입되었으며, EventTarget.prototype.addEventListener
를 사용해 이벤트 핸들러를 도입한다.
EventTarget.addEventListener('eventType', functionName [, useCapture]);
// useCapture capture 사용 여부
// true capturing
// false bubbling(기본값)
이벤트 핸들러 프로퍼티 방식과 달리 addEventListener
는 하나 이상의 이벤트를 등록할 수 있다. 이벤트 핸들러는 등록순으로 호출되며, 참조가 동일한 이벤트 핸들러는 하나만 등록된다.
40.4. 이벤트 핸들러 제거
EventTarget.prototype.removeEventListener
로 addEventListener로 등록한 이벤트 핸들러를 제거할 수 있다. addEventListener
에 전달한 인수와 일치하지 않으면 제거되지 않는다.
// 무명 함수는 참조할 수 없어 제거할 수 없음
$button.addEventListener('click', () => console.log('click'))
// 기명 이벤트 핸들러 내부에서 이벤트 핸들러를 제거할 수 있음
$button.addEventListener('click', function foo() {
console.log('click')
// 이벤트 핸들러를 제거하므로 이벤트 핸들러는 단 한 번만 호출
$button.removeEventListener('click', foo)
})
// 함수 자신을 가리키는 arguments.callee를 사용할 수 있음
$button.addEventListener('click', function () {
console.log('click')
$button.removeEventListener('click', arguments.callee)
})
- arguments.callee는 최적화를 방해하므로 strict mode에서 사용이 금지
- 이벤트 핸들러 프로퍼티 방식으로 등록한 이벤트 핸들러는 removeEventListener로 제거할 수 없다. 제거하려면 이벤트 핸들러 프로퍼티에
null
을 할당한다.
40.5. 이벤트 객체
이벤트가 발생하면 동적으로 생성되어 이벤트 핸들러에 첫 번쨰 인수로 전달된다. 이벤트 객체를 전달받으려면 매개변수를 명시적으로 선언해야 한다.
이벤트 핸들러 어트리뷰트 방식은 이벤트 핸들러의 첫 번째 매개변수 이름이 반드시 event여야 한다. 이벤트 핸들러 어트리뷰트 값은 암묵적으로 생성되는 이벤트 핸들러의 함수 몸체를 의미하기 때문이다.
functino onclick(event) {
showCoords(event)
}
40.5.1. 이벤트 객체의 상속 구조
Event
, UIEvent
, MouseEvent
등 생성자 함수를 호출하여 이벤트 객체를 생성할 수 있다.
40.5.2. 이벤트 객체의 공통 프로퍼티
공통 프로퍼티 | 설명 | 타입 |
---|---|---|
type | 이벤트 타입 | string |
target | 이벤트를 발생시킨 DOM 요소 | DOM 요소 노드 |
currentTarget | 이벤트 핸들러가 바인딩된 DOM 요소 | DOM 요소 노드 |
eventPhase | 이벤트 전파 단계 0: 이벤트 없음, 1: 캡처링 단계, 2: 타깃 단계, 3: 버블링 단계 | number |
bubbles | 이벤트를 버블링으로 전파하는지 여부 다음 이벤트는 bubbles: false로 버블링하지 않는다. - 포커스 이벤트 focus/blur - 리소스 이벤트 load/unload/abort/error - 마우스 이벤트 mouseenter/mouseleave | boolean |
cancelable | preventDefault를 호출해 이벤트의 기본 동작을 취소할 수 있는지 여부 다음 이벤트는 cancelable: false로 취소할 수 없다. - 포커스 이벤트 focus/blur - 리소스 이벤트 load/unload/abort/error - 마우스 이벤트 dbclick/mouseenter/mouseleave | boolean |
defaultPrevented | preventDefault를 호출해 이벤트를 취소했는지 여부 | boolean |
isTrusted | 사용자의 행위에 의해 발생한 이벤트인지 여부, click 메서드 또는 dispatchEvent 등 인위적으로 발생시킨 이벤트인 경우 false | boolean |
timeStamp | 이벤트가 발생한 시각(밀리초) | number |
40.5.3. 마우스 정보 취득
MouseEvent 타입 이벤트 객체의 고유 프로퍼티
- 마우스 포인터의 좌표 정보를 나타내는 프로퍼티: screenX/screenY, clientX/clientY, pageX/pageY, offsetX/offsetY
- 버튼 정보를 나타내는 프로퍼티: altKey, ctrlKey, shiftKey, button
clientX/clientY는 뷰포트를 기준으로 마우스 포인터 좌표를 나타낸다.
40.5.4. 키보드 정보 취득
MouseEvent 타입 이벤트 객체의 고유 프로퍼티
- 고유 프로퍼티: altKey, ctrlKey, shiftKey, key, keyCode
input 요소에 한글을 입력하고 엔터를 누르면 keyup이 두 번 호출되는 현상이 있다. 회피하려면 keydown을 캐치한다.