HTML 문서의 각 엘리먼트들은 아래와 같이 태그 안의 태그가 위치하는 식으로 계층적으로 이루어짐을 볼 수 있다.
이러한 특징으로 인해 HTML 요소에 이벤트가 발생하면 연쇄적으로 이벤트 흐름이 일어나게 된다.
<form onclick="alert('form')">FORM
<div onclick="alert('div')">DIV
<p onclick="alert('p')">P</p>
</div>
</form>
이러한 현상을 이벤트 전파(Event Propagation) 이라 부르며, 전파 방향에 따라 버블링과 캡쳐링으로 구분한다.
표준 DOM 이벤트에서 정의한 이벤트 흐름엔 다음 3가지 단계가 있다.
아래는 HTML 문서의 테이블 안의 <td>
를 클릭하면 어떻게 이벤트가 흐르는지에 대한 사진이다.
<td>
를 클릭하면 이벤트가 최상위 조상인 Window에서 시작해 타깃까지 이벤트가 전파된다. (캡쳐링 단계)이처럼 브라우저는 사용자로부터 이벤트가 발생하면 가장 상단의 요소부터 하위의 요소까지 내려오고, 다시 거슬러 올라가는 식으로 이벤트를 전달하여 발생하도록 한다.
만일, 타깃 요소까지 이벤트를 전파하는 과정에서 그의 부모, 조상에도 이벤트 리스너가 등록되어 있다면 실행되게 된다.
이벤트 위임
이벤트 전파 개념이 없다면,<ul>
태그 안의<li>
요소마다 일일이 이벤트를 등록해야 하는 노가다를 펼쳐야 할지도 모른다.
하지만, 버블링의 특성을 통해 우리는 그 부모 요소인<ul>
요소에만 이벤트를 등록하면, 몇 번째의<li>
요소를 클릭하든 부모 요소로 이벤트가 전파되기 때문에 원하는 구현을 간단하게 할 수 있다.
이 기법을이벤트 위임
이라 부른다.
이벤트 버블링은 이벤트가 전파되는 동안 부모 요소에서도 이벤트를 감지할 수 있다는 장점이 있다.
즉, 이벤트를 처리하는 핸들러 함수를 부모 요소에 등록해놓으면, 자식 요소에서 발생한 이벤트도 모두 처리할 수 있다.
이를 활용하면, 여러 개의 하위 요소에서 발생하는 이벤트를 하나의 이벤트 핸들러로 바인딩하는 이벤트 위임 처리를 할 수 있다.
버블링의 반대이며, 실무에서 자주 쓰이지는 않지만 가끔 유용한 경우가 있다.
자바스크립트 addEventListener()
함수의 3번째 매개변수로 true
값을 주면 이 이벤트 타겟은 캡처링을 통해 이벤트를 전파받아 호출되게 된다.
element.addEventListener('click', (e) => { ... }, true);
element.addEventListener('click', (e) => { ... }, {capture: true});
부모와 자식 둘 다 이벤트를 등록한 상태에서 자식 요소만 클릭했을 때만 이벤트를 발생하고, 부모 요소는 이벤트를 발생시키고 싶지 않은 상황에서 문제가 발생하게 된다.
브라우저는 기본적으로 캡처링-버블링으로 동작하기 때문에, 이벤트 동작 자체를 바꿀 순 없으며, 부모 요소 영역 안에 자식 요소가 위치하기 때문에 논리적으로, 구조적으로 부모에게 이벤트가 전파되는 것이 옳다.
따라서 엘리먼트의 이벤트 전파 처리를 방지하는 식으로 해결해야 한다.
버블링 또는 캡처링 설정에 따라 상위, 하위로 가는 이벤트 전파를 막을 수 있다.
이벤트 전파와 형제 이벤트 실행을 중지한다.
child 요소에 이벤트 리스너가 2개 이상 등록되어 있을 때, 어떠한 조건에서 클릭 이벤트가 여러 번 실행되지 않고 한 번만 실행하도록 원한다면 유용하다.
child.addEventListener("click", (e) => {
if(조건)
e.stopImmediatePropagation()
print('child')
})
child.addEventListener("click", (e) => {
print('child 2')
})
직접 조건 분기를 통해 지정해 줄 수 있다.
e.target
은 실제 이벤트가 걸린 DOM 엘리먼트 객체이고, 그 안의 tagName
, className
, id
속성을 통해 태그의 정보를 분류할 수 있다.
document.body.addEventListener('click', (e) => {
if (e.target.id === "ancestor") {
print('ancestor')
}
if (e.target.id === "parent") {
print('parent')
}
if (e.target.id === "child") {
print('child')
}
});
이벤트 전파 뿐만 아니라 기본 이벤트 동작 자체를 취소한다.
이벤트 전파 중지 + 형제 이벤트 실행 중지 + 이벤트 기본 동작 중지
예를 들어, <form>
태그는 submit시 새로고침된다.
이게 form 태그의 기본 동작이다.
이를 e.preventDefault()
코드를 추가하여 방지할 수 있다.