What I Learned Today
브라우저의 기본 작동을 방지해야 할 경우에는 어떻게 해야할까.
이벤트 객체의 preventDefault() 메서드를 실행해야 한다.
이때 브라우저는 이벤트 객체의 defaultPrevented 속성을 true로 바꿔준다.
요소.addEventListener('click', (e) => {
e.preventDefault()
console.log(e.defaultPrevented) // true
})
이벤트 전파는 세가지 단계가 순차적으로 발생한다. (캡쳐링 -> 타겟 -> 버블링)
이벤트 버블링 - 자식에서 부모로 올라가는 형태로 이벤트가 전파된다. (window까지)
대부분의 이벤트는 버블링으로 처리된다.
그래서 이벤트가 있는 요소를 클릭하면 부모의 요소까지 이벤트가 전파된다.
이벤트 캡쳐링 - 부모에서 자식으로 내려가는 형태로 이벤트가 전파된다.
때때로 이벤트 버블링 단계에서 전파되는 것을 방지해야 할 때가 있다.
이런 경우 stopPropagation() 또는 stopImmediatePropagation() 메서드를 사용한다.
- stopPropagation() : 이벤트가 상위로 전파되는 것을 방지
- stopImmediatePropagation() : 이벤트 상위 전파와 후속 이벤트를 방지
<ul class="link-list">
<li><a href="/news">📢 최신 뉴스 보기</a></li>
<li><a href="/tutorials">📘 튜토리얼 모음</a></li>
<li><a href="/community">💬 커뮤니티 참여</a></li>
<li><a href="/resources">📁 개발 자료실</a></li>
<li><a href="/contact">📮 문의하기</a></li>
</ul>
// li 태그에 포함된 모든 a 태그에 클릭 이벤트 리스너를 추가하려면 이렇게 작성해야 한다.
const links = Array.from(document.querySelectorAll('.link-list a'))
links.forEach((link) => {
link.addEventListener('click, (e) => {
e.preventDefault()
const href = e.currentTarget.getAttribute('href')
console.log(href)
})
})
그런데 만약 링크 갯수가 10000개라면 10000번 반복하여 이벤트 리스너를 만들것이다. 너무 비효율적이지 않은가?
이럴때 사용하는것이 이벤트 위임이다.
이벤트 위임 방법은 이벤트 전파를 활용한다.
- 이벤트 위임 방법
- 상위 요소에 하나의 이벤트 리스너를 추가한다.
- 상위 요소는 하위 요소의 모든 이벤트를 수신한다.
- 단 버블링 단계의 이벤트에서만 작동한다.
// 이벤트 타겟 결정 - 이벤트를 발생시키는 요소, 이벤트 객체의 target 속성으로 접근할 수 있다.
const linkList = document.querySelector('.link-list')
linkList.addEventListener('click', (e) => {
const target = e.target
console.log(target)
// 타겟 매칭 검사
// 현재 이벤트 리스너가 추가된 요소의 모든 하위 요소는 이벤트 타겟이 된다.
// 그러므로 정확히 이벤트가 발생된 대상이 맞는 지 검사가 필요하다.
// 정확히 매칭되는 지 검사할 때는 matches() 메서드를 사용한다.
if (target.matches('a')) {
e.preventDefault()
console.log('링크 요소를 클릭했습니다.')
}
})
만약 중첩된 요소는 어떻게 처리할까?
<ul class="link-list">
<li>
<a href="/news">
<svg
xmlns="http://www.w3.org/2000/svg"
width="24" height="24"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M15 18h-5"/>
<path d="M18 14h-8"/>
<path d="M4 22h16a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v16a2 2 0 0 1-4 0v-9a2 2 0 0 1 2-2h2"/>
<rect width="8" height="4" x="10" y="6" rx="1"/>
</svg>
<span>최신 뉴스 보기</span>
</a>
</li>
<!-- ... -->
</ul>
이런 경우에는 링크의 아이콘이나 텍스트를 클릭했을 때 <svg> 또는 <span> 요소에서 이벤트가 발생한다.
하지만 사용자는 <a herf> 요소를 찾는 것이 목적일 것이다.
- 이때 <a href> 요소를 찾는 방법
- pointer-events: none 스타일 설정 (일반적으로 많이 사용됨)
- closest() 메서드 사용
// example.css
// pointer-events 사용
// 이렇게 하면 a의 자식 요소들은 모두 마우스 이벤트가 발생하지 않는다.
.link-list {
a {
* {
pointer-events: none;
}
}
}
// example.js
// closest() 메서드 사용
// 이렇게 하면 <ul>에서 전달된 <a> 요소를 찾는다.
// 그리고 일치하는 요소를 찾으면 요소를 반환하고 아니면 null을 반환한다.
const linkList = document.querySelector('.link-list')
linkList.addEventListener('click', (e) => {
const target = e.target.closest('a')
if (target) {
...
}
})
이벤트 리스너를 제거하는 방법도 있다.
// 기본 사용법
요소.removeEventListener('이벤트 타입', 리스너)
// 활용
const button = document.querySelector('button')
const handleClick = (e) => {
console.log('버튼 클릭!')
}
button.addEventListener('click', handleClick) // 이벤트 리스너 추가
button.removeEventListener('click', handleClick) // 이벤트 리스너 제거
The Problem I Faced
What I Tried to Do
How I Solved It
So
Aha Moments
반응형