이벤트 캡처링 : 이벤트가 상위요소에서 하위 요소 방향으로 전파
이벤트 타깃 : 이벤트가 타깃에 도달
이벤트 버블링 : 이벤트가 하위요소에서 상위 요소 방향으로 전파
이벤트가 발생했을 때 호출될 함수를 이벤트 핸들러라 하며, 이벤트가 발생했을 때 브라우저에게 이벤트 핸들러의 호출을 위임하는 것을 이벤트 핸들러 등록이라 한다.
이렇게 사용자와 애플리케이션이 상호작용하면서 프로그램의 흐름을 이벤트 중심으로 제어하는 프로그래밍 방식을 이벤트 드리븐 프로그래밍 이라 한다.
마우스, 키보드, 포커스, 폼, 값 변경, DOM 뮤테이션, 뷰, 리소스
const $button = document.querySelector('button');
// 이벤트 핸들러 프로퍼티에 이벤트 핸들러를 바인딩
$button.onclick = function () {
console.log('button click');
};
이벤트 핸들러 프로퍼티에 이벤트 핸들러를 바인딩하는 방식으로, 하나의 이벤트 핸들러만 바인딩할 수 있다는 단점이 있다. 만약 두 개의 이벤트를 바인딩한다면 재할당되어 기존의 이벤트는 실행되지 않는다.
이벤트 핸들러 프로퍼티 방식으로 등록된 이벤트 핸들러를 제거하려면 이벤트 핸들러 프로퍼티에 null을 할당하면 된다.
$button.addEventListener('click', function () {
console.log('button click');
});
addEventListener 방식은 캡처링 또는 버블링을 지정할 수 있으며 이벤트 핸들러 프로퍼티 방식과 달리 여러 개의 이벤트를 등록할 수 있다. 이때 동일한 이벤트를 여러번 등록하면 하나의 이벤트 핸들러만 등록된다.
addEventLister 메서드로 등록한 이벤트 핸들러를 제거하기 위해서는 EventTarget.prototype.removeEventLister
메서드를 사용해야 하는데, 전달한 인수가 일치해야만 제거된다. 따라서 무명함수를 등록한다면 제거할 수 없고, 이벤트 핸들러를 제거하려면 이벤트 핸들러의 참조를 변수나 자료구조에 저장하고 있어야 한다.
이벤트 객체는 이벤트 핸들러의 첫 번째 인수로 전달되어 매개변수 e에 암묵적으로 할당된다.
input 요소의 입력 필드에 한글을 입력하고 엔터키를 누르면 keyup 이벤트 핸들러가 두 번 호출되는 현상이 발생한다. keydown 이벤트를 사용하면 해결할 수 있다.
이벤트가 발생하여 생성된 이벤트 객체는 이벤트를 발생시킨 DOM 요소인 이벤트 타겟을 중심으로 DOM 트리를 통해 전파된다. 이 때 전파되는 방향에 따라 다음과 같이 구분된다.
이벤트 핸들러 어트리뷰트/프로퍼티 방식으로 등록한 이벤트 핸들러는 타깃 단계와 버블링 단계의 이벤트만 캐치할 수 있지만, addEventLinstenter 메서드 방식으로 등록한 이벤트 핸들러는 3번 째 인수를 사용하여 캡처링 단계의 이벤트를 캐치할 수 있다.
stopPropagation 메서드(e.stopPropagation();
)는 이벤트 전파를 중지시킨다.
<ul id="fruits"> <li id="apple" > <li ... > <li ... > <li ... > </ul>
<div> 선택된 아이템: <em class="msg"">apple</em>/div>
function activate ({ target }) {
if(!target.matches('#fruits > li')) return;
[...$fruits.children].forEach($fruit => {
$fruit.classList.toggle('active', $fruit === target);
$msg.textContent = target.id;
});
}
$fruits.onclick = activate;
여러 개의 하위 DOM 요소에 이벤트 핸들러를 등록하는 대신, 상위 DOM 요소에 이벤트를 등록할 수 있다. 이때 Element.prototype.matches 메서드로 반응이 필요한 DOM 요소에만 한정하는 것이 좋다.
이벤트 핸들러 어트리뷰트 방식의 this는 전역객체 window를 가리킨다. 단 이벤트 핸들러를 호출할 때 전달한 this는 이벤트를 바인딩한 DOM 요소를 가리킨다.
하지만 이벤트 핸들러 프로퍼티 방식과 addEventListener 메서드 방식의 this는 이벤트를 바인딩한 DOM 요소를 가리킨다. 즉 이벤트 핸들러 내부의 this는 이벤트 객체의 currentTarget 프로퍼티와 같다.
화살표 함수는 함수 자체 this 바인딩을 갖지 않기 때문에 화살표함수로 정의한 이벤트 핸들러 내부의 this는 상위스코프의 this를 가리킨다.
이벤트 핸들러 어트리뷰트 방식은 함수 호출문을 사용할 수 있기 때문에 인수를 전달할 수 있지만, 이벤트 핸들러 프로퍼티 방식과 addEventListener 메서드 방식의 경우 이벤트 핸들러를 브라우저가 호출하기 때문에 호출문이 아닌 함수 자체를 등록해야 하므로, 인수를 전달할 수 없다.
$input.onblur = () => {
checkUserNameLength(MIN_USER_NAME_LENGTH);
};
const checkUserNameLength = min => e => {
$msg.text Content = $input.value.length < min ? `${min}자 이상 입력필요` : '';
};
$input.onblur = checkUserNameLength(MIN_USER_NAME_LENGTH);
이 때 인수를 전달하기 위해서는 위와 같이 이벤트 핸들러 내부에서 함수를 호출하면서 인수를 전달하거나 이벤트 핸들러를 반환하는 함수를 호출하면서 인수를 전달할 수도 있다.
const customEvent = new CustomEvent('foo');
console.log(customEvent.type); // foo
const keyboardEvent = new KeyboardEvent('keyup');
console.log(keyboardEvent.type); // keyup
이벤트 객체는 Event, UIEvent, MouseEvent 같은 이벤트 생성자 함수로 생성할 수 있는데, 이렇게 이벤트 생성자 함수를 호출하여 명시적으로 생성한 이벤트 객체는 임의의 이벤트 타입을 지정할 수 있다. 이처럼 개발자의 의도로 생성된 이벤트를 커스텀 이벤트라 한다.
커스텀이벤트는 첫번째 인수로 이벤트 타입을 나타내는 문자열을 전달받는데, 임의의 문자열도 사용할 수 있다. 이 경우 일반적으로 CustomEvent 이벤트 생성자 함수를 사용한다.
커스텀 이벤트 객체에는 이벤트 타입에 따라 가지는 이벤트 고유의 프로퍼티 값을 지정할 수도 있다.
$button.addEventListener('foo', e => {
alert(e.detail.message; // CustomEvent 함수의 두 번째 인수로 전달한 정보가 담겨있다.
});
const customEvent = new CustomEvent('foo', {
detail: { message: 'Hello' { // detail 프로퍼티
});
const customEvent = new CustomEvent('foo');
console.log(customEvent.type); // foo
$button.dispatchEvent(customEvent);
커스텀 이벤트는 dispatchEvent 메서드로 디스패치(이벤트를 발생시키는 행위)를 할 수 있다. 일반적으로 이벤트 핸들러는 비동기 처리방식으로 동작하지만 dispatchEvent 메서드는 동기처리 방식으로 호출한다. 따라서 이벤트를 디스패치 하기 이전에 커스텀 이벤트를 처리할 이벤트 핸들러를 등록해놔야만 한다.
CustomEvent 이벤트 생성자 함수에 두 번째 인수로 이벤트와 함께 전달하고 싶은 정보를 담은 detail 프로퍼티를 포함하는 객체를 전달할 수 있다.
기존 이벤트 타입이 아닌 임의의이벤트 타입 지정시 반드시 addEventListener 메서드 방식으로 이벤트 핸들러를 등록해야 하는데, ‘on + ~~’ 라는 요소 노드에 존재하지 않기 때문이다.