-
[JavaSript] 버블링, 캡쳐링, 이벤트위임JavaScript 2024. 1. 30. 17:50728x90반응형
버블링, 캡쳐링, 이벤트 위임
버블링
- 버블링이란?
- 안쪽에 있는 자식 요소에서 시작해 부모 요소로 거슬러 올라가며 이벤트가 발생하는 것을 버블링이라고 함.
- 모든 이벤트는 버블링 됨(default)
focus와 같이 버블링 되지 않는 몇몇의 이벤트를 제외하고는 대부분의 이벤트는 버블링이 됨
버블링 예시
<style> body * { margin: 10px; border: 1px solid blue; } </style> <form onclick="alert('form')">FORM <div onclick="alert('div')">DIV <p onclick="alert('p')">P</p> </div> </form>
가장 안쪽에 위치한 p 태그를 클릭하면 아래와 같이 작동함
- p 태그의 onClick 핸들러가 동작함
- 바깥 태그인 div의 onClick 핸들러가 동작함
- 그 위의 태그인 form에 할당된 onClick 핸들러가 동작함
- document 객체(DOM tree의 가장 최상위 노드(객체))를 만날 때까지 각 요소에 할당된 onClick 핸들러가 동작함
=> 따라서 p만 눌렀을 뿐인데 3개의 alert 창('p' -> 'div' -> 'form')이 뜸
버블링 중단 예시
핸들러에게 이벤트를 완전히 처리하고 난 후 버블링을 중단하도록 명령할 수 있음
바로event.stopPropagation()
을 사용하면 됨<style> body * { margin: 10px; border: 1px solid blue; } </style> <form onclick="alert('form')">FORM <div onclick="alert('div')">DIV <p onclick="event.stopPropagation()">P</p> </div> </form>
p를 클릭해도 div의 onClick은 동작하지 않음
event.stopPropagation()과 event.stopImmediatePropagation()의 차이점
- event.stopPropagation()
현재 이벤트의 버블링 단계에서 이벤트 전파를 중단함. 즉, 현재 요소에서 발생한 이벤트가 상위 요소로 전파되는 것을 막음 - event.stopImmediatePropagation()
event.stopPropagation()과 비슷하지만 같은 이벤트 타입을 가진 핸들러들도 호출되지 않도록 함. 즉, 버블링이나 캡쳐링을 멈추고, 해당 요소에 등록된 다른 이벤트 핸들러들도 막을 때 사용하면 됨.
이 메소드를 사용하면 요소에 할당된 특정 이벤트를 처리하는 이벤트 핸들러가 모두 동작하지 않음
예시
<body> <div id="outer"> Outer <button id="inner">Inner</button> </div> <script> document.querySelector("#outer").addEventListener("click", () => { alert("outer clicked!"); }); document.querySelector("#inner").addEventListener("click", (event) => { event.stopPropagation() alert("inner clicked - First Handler"); }); document.querySelector("#inner").addEventListener("click", (event) => { alert("inner clicked - Second Handler"); }); </script>
event.stopPropagation()을 첫 번째 이벤트 핸들러에 사용한 경우 outer clicked alert는 발생하지 않고 button에 등록된 이벤트 핸들러인 inner clicked - First Handler와 inner clicked - Second Handler만 나타날 것임.
즉, 부모 요소인 #outer로의 이벤트 전파(event propagation)은 막지만 동일한 내부의 다른 이벤트 핸들러는 계속 실행됨
그렇다면 부모 요소로의 이벤트 버블링도 막고 같은 요소 내의 다른 이벤트 핸들러도 막으려면 어떻게 해야할까? 바로 event.stopImmediatePropagation()를 사용하면 됨
<body> <div id="outer"> Outer <button id="inner">Inner</button> </div> <script> document.querySelector("#outer").addEventListener("click", () => { alert("outer clicked!"); }); document.querySelector("#inner").addEventListener("click", (event) => { event.stopImmediatePropagation() alert("inner clicked - First Handler"); }); document.querySelector("#inner").addEventListener("click", (event) => { alert("inner clicked - Second Handler"); }); </script>
#inner의 첫 번째 이벤트 핸들러에 event.stopImmediatePropagation()를 사용하면
"inner clicked - First Handler"라는 alert만 발생하고 #inner의 두 번째 이벤트 핸들러와 부모 요소인 outer의 이벤트도 발생하지 않음.즉, stopImmediatePropagation()는 부모 요소인 #outer로의 이벤트 전파 뿐만 아니라 동일한 요소 내부의 다른 모든 이벤트 핸들러도 중단시킴
캡쳐링
- 캡쳐링이란?
자식요소에서 발생한 이벤트가 부모 요소로부터 시작해 이벤트가 발생한 안쪽 자식 요소까지 도달하는 것을 캡쳐링이라 함
표준 DOM 이벤트에서 정의한 이벤트 전파 흐름에는 3가지 단계가 있음
- 캡쳐링 단계 : 이벤트가 하위 요소로 전파되는 단계
- 타깃 단계 : 이벤트가 실제 타깃 요소로 전달되는 단계
- 버블링 단계 : 이벤트가 상위 요소로 전파되는 단계
위의 이미지처럼 td 태그를 클릭하면 이벤트가 최상위 조상에서 시작해 아래로 전파(캡쳐링단계) -> 이벤트가 타깃 요소에 도착해 실행(타깃단계) -> 다시 위로 전파(버블링 단계)
이 과정을 통해 할당된 이벤트 핸들러가 호출됨
캡처링 단계를 이용해야하는 경우는 많지 않은데, 캡쳐링 단계에서 이벤트를 잡아내려면 addEventListener의 capture 옵션을 true로 설정하면 됨(default는 false로, 버블링 단계에서 이벤트를 잡음)elem.addEventListener(..., {capture: true}) 혹은 아래와 같이 true만 써도 됨 elem.addEventListener(..., true)
예시
예시
<style> body * { margin: 10px; border: 1px solid blue; } </style> <form>FORM <div>DIV <p>P</p> </div> </form> <script> for(let elem of document.querySelectorAll('*')) { elem.addEventListener("click", e => alert(`캡쳐링: ${elem.tagName}`), true); elem.addEventListener("click", e => alert(`버블링: ${elem.tagName}`)); } </script>
위의 예시에서 p 태그를 클릭하면 이벤트 흐름이 어떻게 되는지 살펴보자면 아래와 같음
- HTML -> BODY -> FORM -> DIV (캡처링 단계, 첫 번째 이벤트 리스너가 실행되면서 왼쪽과 같이 alert가 발생함)
- p (타깃 단계, 캡처와 버블링 둘 다에 이벤트 리스너를 설정했기 때문에 두 번 호출됨)
- DIV -> FORM -> BODY -> HTML (버블링 단계, 두 번째 이벤트 리스너가 실행되면서 왼쪽과 같이 alert가 실행됨)
이벤트 위임(event delegation)
- 여러 개의 하위 DOM 요소에 각각 이벤트 핸들러를 등록하는 대신, 하나의 상위 DOM 요소에 이벤트 핸들러를 등록하는 이벤트 핸들링 패턴
- 이벤트 위임을 사용하면 요소마다 이벤트 핸들러를 할당하지 않고 공통의 상위 요소에 이벤트 핸들러를 단 하나만 할당해도 여러 요소를 한꺼번에 다룰 수 있음(이벤트 전파(버블링, 캡쳐링을 사용해서))
만약 이벤트 위임이 없다면 모든 요소에 일일히 이벤트를 등록해야함
-> 하지만 이벤트 위임이 있기 때문에 하나의 부모 요소에만 핸들러를 등록함으로써 코드의 중복을 줄이고 메모리 사용량을 줄일 수 있음
ex: 리스트 아이템이 수백 개 있는 목록에서 각각의 아이템(<li>
)에 클릭 이벤트 핸들러를 붙이는 것보다 리스트 전체(<ol>
)에 한 번만 클릭 핸들러를 붙여서 클릭된 아이템을 판별하는 것이 효율적임예시
<!DOCTYPE HTML> <html> <head> <link rel="stylesheet" href="messages.css"> <meta charset="utf-8"> </head> <body> <div id="container"> <div class="pane"> <h3>Horse</h3> <p>The horse is one of two extant subspecies of Equus ferus. It is an odd-toed ungulate mammal belonging to the taxonomic family Equidae. The horse has evolved over the past 45 to 55 million years from a small multi-toed creature, Eohippus, into the large, single-toed animal of today.</p> <button class="remove-button">[x]</button> </div> <div class="pane"> <h3>Donkey</h3> <p>The donkey or ass (Equus africanus asinus) is a domesticated member of the horse family, Equidae. The wild ancestor of the donkey is the African wild ass, E. africanus. The donkey has been used as a working animal for at least 5000 years.</p> <button class="remove-button">[x]</button> </div> <div class="pane"> <h3>Cat</h3> <p>The domestic cat (Latin: Felis catus) is a small, typically furry, carnivorous mammal. They are often called house cats when kept as indoor pets or simply cats when there is no need to distinguish them from other felids and felines. Cats are often valued by humans for companionship and for their ability to hunt vermin. </p> <button class="remove-button">[x]</button> </div> </div> <script> container.onclick = function(event) { if (event.target.className != 'remove-button') return; let pane = event.target.closest('.pane'); pane.remove(); }; </script> </body> </html>
이벤트 위임을 사용해서 단 하나의 이벤트 리스너만으로 삭제 버튼이 동작하도록 구현한 코드
라는 상위 요소에 클릭 이벤트를 걸어두고, 하위 요소에서 이벤트가 발생하면 해당 이벤트가 버튼에서 시작해서 상위 요소로 전달되어 결국 container까지 도달함(이벤트 버블링 사용됨)예시 출처 : https://ko.javascript.info/event-delegation 과제 1번
반응형'JavaScript' 카테고리의 다른 글
[JavaScript] 얕은 복사, 깊은 복사 (0) 2024.01.28 [JavaScript] 콜백함수, 비동기 (0) 2024.01.27 [JavaScript] This, Closure (0) 2024.01.21 [JavaScript] 실행 컨텍스트 (0) 2024.01.17 [Javascript] variables, hoisting (0) 2024.01.15 - 버블링이란?