모달창에서 활용하는 이벤트 버블링의 원리
프로젝트를 진행하며 모달창을 구현하는 와중, 이벤트 버블링이라는 개념에 대해 자세히 학습하고자 해당 포스트로 정리해보았다.
이벤트 버블링(Event Bubbling)은 자바스크립트에서 발생한 이벤트가 DOM 트리 구조에서 하위 요소에서 상위 요소로 전파되는 방식이다.
쉽게 말해, 특정 요소에서 이벤트가 발생하면 그 이벤트가 상위 요소들로 전파되어 상위 요소에서도 동일한 이벤트를 처리할 수 있는 메커니즘이다.
주로 어떤 경우에 쓰이는 지 알아보자면,
- 여러 중첩된 요소가 이벤트를 공유할 때: 예를 들어, 클릭 이벤트가 발생했을 때, 자식 요소와 부모 요소 모두에서 처리해야 하는 경우가 있을 수 있다. 이때 이벤트 버블링을 이용하면 자식 요소에서 이벤트가 발생해도 부모 요소까지 이벤트가 전파되기 때문에 이벤트를 여러 단계에서 처리할 수 있다.
- 이벤트 위임(Event Delegation): 많은 요소에 각각 이벤트 리스너를 달지 않고, 상위의 공통 부모 요소에 이벤트 리스너를 추가하는 패턴이다. 예를 들어, 리스트 항목이 동적으로 추가되거나 제거되는 상황에서 각각의 항목에 이벤트 리스너를 붙이기보다는 부모 요소에 이벤트를 위임하는 것이 효율적이므로, DOM에 많은 이벤트 리스너를 붙일 필요 없이 부모 요소 하나에서 처리할 수 있다.
- 부모 요소에 이벤트를 처리해야 하는 경우: 예를 들어, 모달 창에서 바깥 영역을 클릭하면 모달이 닫히게 하고 싶을 때, 이벤트가 자식(모달 내부)에서 발생해도 부모(모달 외부)로 버블링되기 때문에 이를 활용해 모달을 닫는 로직을 만들 수 있다.
이벤트 버블링의 동작 방식
DOM에서 요소들은 계층 구조(트리 구조)로 존재한다. 예를 들어, 아래와 같은 HTML 구조가 있다고 가정해보자!
<div id="parent">
<div id="child">
<button id="button">Click Me</button>
</div>
</div>여기서 button 요소를 클릭하면 이벤트가 아래와 같은 순서로 발생하게 된다.
- button 요소에서 클릭 이벤트가 발생
- 이 이벤트는 상위 요소(child)로 전파
- 이후 상위의 상위 요소(parent)로 전파
이렇게 이벤트가 하위에서 상위로 전파되는 과정을 이벤트 버블링이라고 한다.
다음 자바스크립트 코드를 보면 이벤트 버블링을 쉽게 이해할 수 있다.
<div id="parent">
<div id="child">
<button id="button">Click Me</button>
</div>
</div>
<script>
document.getElementById('parent').addEventListener('click', function() {
console.log('Parent clicked');
});
document.getElementById('child').addEventListener('click', function() {
console.log('Child clicked');
});
document.getElementById('button').addEventListener('click', function() {
console.log('Button clicked');
});
</script>이 코드에서 button 요소를 클릭하면 콘솔에는 다음과 같은 출력이 나온다.
- Button clicked
- Child clicked
- Parent clicked
이처럼 이벤트가 button에서 시작하여 child를 거쳐 parent까지 전파되는 것을 확인할 수 있다.
이벤트 캡처링(Event Capturing)
이벤트 버블링과 반대되는 개념으로 이벤트 캡처링(Event Capturing)이 있다.
이벤트 캡처링은 상위 요소에서 하위 요소로 이벤트가 전달되는 방식이 있다. 기본적으로 브라우저는 이벤트를 캡처링 단계에서 시작해 버블링 단계로 마무리하는데, 캡처링 단계는 잘 사용되지 않으며, 대부분의 이벤트는 버블링 단계에서 처리된다.
이벤트 리스너를 설정할 때, 세 번째 인자로 true를 설정하면 캡처링 단계에서 이벤트가 발생하도록 할 수 있다.
document.getElementById('parent').addEventListener('click', function() {
console.log('Parent clicked');
}, true);위 코드에서 true를 추가하면 parent 요소에서 캡처링 단계가 시작되고, Parent clicked가 먼저 출력된다.
이벤트 전파 제어
이벤트 버블링을 제어하는 방법은 크게 두 가지이다.
- event.stopPropagation(): 이 메서드를 사용하면 이벤트가 상위 요소로 전파되지 않도록 할 수 있다. 모달 창과 같은 UI 요소에서 상위 요소로 이벤트가 전달되지 않도록 할 때 유용하다.이 경우, button 클릭 시 Button clicked만 출력되고, child와 parent에서는 더 이상 이벤트가 발생하지 않는다.
- document.getElementById('button').addEventListener('click', function(event) { console.log('Button clicked'); event.stopPropagation(); // 이벤트 버블링을 중단 });
- event.preventDefault(): 이벤트의 기본 동작을 막는 메서드로, 링크의 이동이나 폼 제출 같은 기본 동작을 차단할 때 사용된다. 이벤트 버블링과는 별도로 이벤트의 기본 동작을 차단하는 것이므로 전파를 막지는 않는다.
모달 창에서의 이벤트 버블링 활용
모달 창을 만들 때 이벤트 버블링을 잘 관리하는 것이 중요하다. 예를 들어, 모달 창 바깥 영역을 클릭하면 모달을 닫아야 하고, 모달 내부를 클릭했을 때는 모달이 닫히지 않도록 해야 할 때가 많다. 이를 위해서는 이벤트 버블링을 잘 이해하고, 필요할 경우 전파를 막는 처리가 필요하다.
<div id="modal">
<div id="modal-content">
<!-- 모달 콘텐츠 -->
</div>
</div>
<script>
const modal = document.getElementById('modal');
const modalContent = document.getElementById('modal-content');
// 모달 바깥을 클릭하면 모달을 닫음
modal.addEventListener('click', function() {
modal.style.display = 'none';
});
// 모달 내부 클릭 시 이벤트 전파 중단
modalContent.addEventListener('click', function(event) {
event.stopPropagation(); // 모달 창 내부 클릭 시 모달이 닫히지 않도록
});
</script>위 코드를 보면 modal 요소를 클릭하면 모달이 닫히지만, modal-content를 클릭하면 event.stopPropagation()으로 인해 모달이 닫히지 않는다.
결론
이벤트 버블링은 하위 요소에서 상위 요소로 이벤트가 전파되는 과정이며, 이를 제어하기 위한 메서드인 event.stopPropagation()과 event.preventDefault()를 이해하면 더욱 복잡한 사용자 인터페이스를 효율적으로 설계할 수 있다.
모달 창처럼 상위 요소와 하위 요소의 상호작용을 관리해야 할 때, 이러한 개념을 적절히 활용하는 것이 중요할 것 같다!