- Published on
2024.09.29
[딥다이브] - 40. 이벤트(2)
40.6. 이벤트 전파
DOM 요소 노드에서 발생한 이벤트가 DOM 트리를 통해 전파되는 것
- 캡처링 단계: 이벤트가 상위 요소에서 하위 요소 방향으로 전파
- 타깃 단계: 이벤트가 이벤트 타깃에 도달
- 버블링 단계: 이벤트가 하위 요소에서 상위 요소 방향으로 전파
이벤트 핸들러 어트리뷰트/프로퍼티 방식으로 등록한 이벤트 핸들러는 타깃 단계와 버블링 단계의 이벤트만 캐치할 수 있다.addEventListener
메서드 방식으로 등록한 이벤트 핸들러는 3번째 인수로 true
를 전달하면 캡처링 단계의 이벤트도 선별적으로 캐치할 수 있다.
이벤트는 이벤트가 통과하는 DOM 트리 경로인 이벤트 패스에 위치한 모든 DOM 요소에서 캐치할 수 있다.
- 버블링을 통해 전파되지 않는 이벤트
- 포커스 이벤트: focus/blur
- 리소스 이벤트: load/unload/abort/error
- 마우스 이벤트: mouseenter/mouseleave
40.7. 이벤트 위임
이벤트 위임은 여러 개의 하위 DOM 요소에 각각 이벤트 핸들러를 등록하는 대신 하나의 상위 DOM 요소에 이벤트 핸들러를 등록하는 방법이다. 이벤트 위임을 통해 상위 DOM 요소에 이벤트 핸들러를 등록하면 여러 개의 하위 DOM 요소에 이벤트 핸들러를 등록할 필요가 없다.
이벤트 위임 시 주의할 점은 이벤트 타깃이 개발자가 기대한 DOM 요소가 아닐 수도 있다는 것이다. 따라서 필요한 DOM 요소에 한정하여 실행되도록 검사할 필요가 있다. Event.prototype.matches
는 인수로 전달된 선택자에 의해 특정 노드를 탐색 가능한지 확인한다.
40.8. DOM 요소의 기본 동작 조작
40.8.1. DOM 요소의 기본 동작 중단
preventDefault
는 DOM 요소의 기본 동작을 중단시킨다.
40.8.2. 이벤트 전파 방지
stopPropagation
은 이벤트 전파를 중단시켜 자신에게 바인딩된 이벤트 핸들러만 실행되도록 한다.
40.9. 이벤트 핸들러 내부의 this
40.9.1. 이벤트 핸들러 어트리뷰트 방식
<body>
<button onclick="handleClick(this)">Click me</button>
<script>
function handleClick(button) {
console.log(button) // 이벤트를 바인딩한 button 요소
console.log(this) // window
}
</script>
</body>
일반 함수로서 호출되는 함수 내부의 this는 전역 객체(window)를 가리킨다. 단, 이벤트 핸들러를 호출할 때 인수로 전달한 this는 이벤트를 바인딩한 DOM 요소를 가리킨다. 이는 이벤트 핸들러 프로퍼티 방식과 동일하다.
40.9.2. 이벤트 핸들러 프로퍼티 방식과 addEventListener 메서드 방식
이벤트 핸들러 프로퍼티 방식과 addEventListener
방식 모두 이벤트 핸들러 내부의 this는 이벤트를 바인딩한 DOM 요소를 가리킨다. 즉, 이벤트 핸들러 내부의 this는 이벤트 객체의 currentTarget 프로퍼티와 같다. 화살표 함수는 함수 자체의 this 바인딩을 갖지 않아 상위 스코프의 this를 가리킨다.
<body>
<button class="btn1">0</button>
<script>
const $button1 = document.querySelector('.btn1')
// 이벤트 핸들러 프로퍼티 방식
$button1.onclick = function (e) {
// this는 이벤트를 바인딩한 DOM 요소를 가리킨다.
console.log(this) // $button1
console.log(e.currentTarget) // $button1
}
// addEventListener 방식
$button1.addEventListener('click', function (e) {
// this는 이벤트를 바인딩한 DOM 요소를 가리킨다.
console.log(this) // $button1
console.log(e.currentTarget) // $button1
})
// 이벤트 핸들러 프로퍼티 방식
$button1.onclick = (e) => {
// 화살표 함수 내부의 this는 상위 스코프의 this를 가리킨다.
console.log(this) // window
console.log(e.currentTarget) // $button1
}
</script>
</body>
40.10. 이벤트 핸들러에 인수 전달
함수 자체를 등록해야 하는 이벤트 핸들러 프로퍼티 방식과 addEventListener 방식도 이벤트 핸들러 내부에서 함수를 호출하면서 인수를 전달할 수 있다. 혹은 이벤트 핸들러를 리턴하는 함수를 호출하면서 인수를 전달할 수도 있다.
<body>
<label><input type="text" /></label>
<em class="message"></em>
<script>
const MIN_NAME_LENGTH = 5
const $input = document.querySelector('input[type=text]')
const $msg = document.querySelector('.message')
const checkUserNameLength = (min) => {
$msg.textContent = $input.value.length < min ? ${min}자 이상 입력해 주세요: '';
}
// 이벤트 핸들러 내부에서 함수를 호출하며 인수 전달
$input.onblur = () => {
checkUserNameLength(MIN_NAME_LENGTH)
}
// 이벤트 핸들러를 리턴하는 함수
const checkUserNameLength = (min) => (e)=> {
$msg.textContent = $input.value.length < min ? ${min}자 이상 입력해 주세요: '';
}
$input.onblur = checkUserNameLength(MIN_NAME_LENGTH)
</scrip>
</body>
40.11. 커스텀 이벤트
40.11.1. 커스텀 이벤트 생성
개발자의 의도대로 명시적으로 생성한 이벤트 객체(커스텀 이벤트)는 임의의 이벤트 타입을 지정할 수 있다. 첫 번째 인수로 이벤트 타입을 나타내는 문자열을 전달받는다.
const keyboardEvent = new keyboardEvent('keyup')
console.log(keyboardEvent.typs) // keyup
const customEvent = new CustomEvent('foo')
console.log(customEvent.type) // foo
커스텀 이벤트 객체는 bubbles
와 cancelable
값이 false
로 기본 설정되어 버블링되지 않고 preventDefault
로 취소할 수 없다. 프로퍼티를 true
로 설정하려면 두 번째 인수로 bubbles
나 cancelable
을 갖는 객체를 전달한다.
const customEvent = new MouseEvent('click', { bubbles: true, cancelable: true })
커스텀 이벤트 객체는 이벤트 고유 프로퍼티 값을 지정할 수 있다.
const mouseEvent = new MouseEvent('click', {
bubbles: true,
cancelable: true,
clientX: 50,
clientY: 100,
})
이벤트 생성자 함수로 생성한 커스텀 이벤트는 isTrusted
값이 언제나 false
이며, 커스텀 이벤트가 아닌 사용자의 행위에 의해 발생한 이벤트로 생성된 이벤트 객체는 언제나 true
다.
40.11.2. 커스텀 이벤트 디스패치
디스패치: 이벤트를 발생시키는 행위
커스텀 이벤트는 dispatchEvent
로 디스패치
할 수 있다.
$button.dispatchEvent(customEvent)
일반적으로 이벤트 핸들러는 비동기로 동작하지만 dispatchEvent는 동기 방식으로 호출한다. 즉, dispatchEvent를 호출하면 커스텀 이벤트에 바인딩된 이벤트 핸들러를 직접 호출하는 것과 같다. 따라서 디스패치 하기 전에 이벤트 핸들러를 등록해야 한다.
const customEvent = new CustomEvent('foo')
// 커스텀 이벤트를 디스패치하기 전 이벤트 핸들러 등록
$button.addEventListener('foo', (e) => {
// e.detail에는 CustomEvent 함수의 두 번째 인수로 전달한 정보가 담겨 있다.
alert(e.detail.message)
})
const customEvent = new CustomEvent('foo', {
deatil: { message: 'Hello' }, // 이벤트와 함께 전달하고 싶은 정보
})
// 커스텀 이벤트 디스패치
$button.dispatchEvent(customEvent)