티스토리 뷰

이벤트 객체의 메서드인 Event.stopPropagation() 를 이해하기 위해 일단 이벤트 버블링에 대해 알아보자.

버블링이란?

버블링이란 한 요소에 이벤트가 발생하면, 이 요소에 할당된 핸들러가 동작하고, 이어서 부모 요소의 핸들러가 동작하는 것을 말하며, 가장 최상단의 조상 요소를 만날 때까지 이 과정이 반복되면서 요소 각각에 할당된 핸들러가 동작한다.

간단한 예시를 통해 버블링이 어떻게 발생하는지 알아보자.

<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> 요소를 클릭하면 다음과 같은 순서로 동작이 나타난다.

  1. <p> 에 할당된 onclick 핸들러가 동작함.
  2. <div> 에 할당된 onclick 핸들러가 동작함.
  3. <form> 에 할당된 onclick 핸들러가 동작함.
  4. document 객체를 만날 때까지, 각 요소에 할당된 onclick 핸들러가 동작함

위의 동작 방식에 의해 <p> 요소를 클릭하면  p -> div -> form  순서로 3개의 얼럿 창이 뜨며, 이런 흐름을 '이벤트 버블링'이라 한다.

그렇다면 <p> 요소를 클릭 시에, 이벤트 버블링으로 인해 div와 form 에 적용된 이벤트가 발생하지 않도록 하려면 어떻게 해야 할까? 이때 사용되는 이벤트 객체의 메서드가 event.stopPropagation() 이다. event.stopPropagation() 은 현재 이벤트 이후의 전파를 막아 위쪽으로 일어나는 이벤트 버블링을 막아 준다.

event.stopPropagation()을 활용한 모달

event.stopPropagation() 를 활용하여 다음과 같이 동작하는 모달(Modal)을 만들어 보려고 한다. 

  • 모달 바깥 영역에 onclick 이벤트 시에 closeModal() 함수를 통해 모달 닫기
  • 모달 영역 내의 모달 닫기 버튼이 onclick 이벤트 시에  closeModal() 함수를 통해 모달 닫기

이때, [모달] 영역 내의 [모달 닫기 버튼]을 클릭 시에, 정상적으로 모달이 닫히기는 하지만, 이벤트 버블링 때문에 콘솔을 통해 에러가 발생하는 것을 알 수 있다. [모달 닫기 버튼]을 클릭 시, closeModal() 함수를 통해 모달에 대한 요소를 삭제하는데, 버블링을 통해 모달 바깥 영역에 적용된 closeModal() 함수가 다시 호출되면서, 이미 삭제한 모달요소를 다시 삭제하려니 에러가 발생하는 것이다. 

또한 모달 영역에서 [모달 닫기 버튼] 영역을 제외한 부분을 클릭하면, 버블링 때문에 모달 바깥 영역의 클릭 이벤트가 발생하여 모달이 닫히는 문제가 발생한다.

event.stopPropagation() 적용 전

따라서 이러한 에러를 막기 위해, [모달] 영역에 click 이벤트 시에 event.stopPropagation() 을 적용하자. 그려면, [모달] 영역 밖에서 발생하는 이벤트 버블링을 막아주는 동시에 [모달] 내부에서 [모달 닫기 버튼] 영역을 제외한 부분을 클릭하면 모달이 닫히는 문제를 해결할 수 있다.

event.stopPropagation() 적용 후

 

 

[ 전체 코드 ]

style.css

body{
    position:relative;
}
.open-modal-btn {
    width: 120px;
    height: 40px;
    background-color: aquamarine;
    cursor: pointer;
    border-radius: 20px;
    display: flex;
    justify-content: center;
    align-items: center;
}
.open-modal-btn:hover {
    background-color: aqua;
}
.modal-wrapper {
    position: fixed;
    left: 0;
    bottom: 0;
    top: 0;
    right: 0;
    z-index: 999;
    background-color: rgba(0,0,0,.1);
    display: flex;
    justify-content: center;
    align-items: center;
}
.modal {
    width: 200px;
    background-color: white;
    border: 1px solid gray;
    border-radius: 20px;
}
.modal-content {
    padding: 10px;
    display: flex;
    justify-content: center;
    align-items: center;
}
.modal-content span {
    text-align: center;
}
.close-modal-btn {
    background: transparent;
    cursor: pointer;
    border-top-style: solid;
    border-top-color: gray;
    border-top-width: 1px;
    text-align: center;
    padding: 10px;
}

index.html

<!DOCTYPE html>
<html lang="en">
<head>
<title>Event Propagation</title>
<link rel="stylesheet" href="style.css">

</head>

<body class="container" >
    <div class="open-modal-btn" onclick="openModal();">Open Modal</div>


    <script>
        const bodyElem = document.querySelector('.container');

        const closeModal = (event) => {
            const modalWrapperElem = document.querySelector('.modal-wrapper');
            bodyElem.removeChild(modalWrapperElem)
        }

        const stopPropagation = (e) => {
            e.stopPropagation();
        }
        
        const openModal = () => {
            const modalWrapperElem = document.createElement('div');
            modalWrapperElem.addEventListener("click", closeModal);
            modalWrapperElem.classList.add('modal-wrapper');
            const modalElem = document.createElement('div');
            modalElem.classList.add('modal');
            modalElem.addEventListener('click', stopPropagation);
        
            const modalContentElem = document.createElement('div');
            modalContentElem.classList.add('modal-content');
            modalContentElem.innerHTML = `
            <span>This is Modal</span>
            `
            const closeBtn = document.createElement('div')
            closeBtn.classList.add('close-modal-btn')
            closeBtn.innerHTML = "닫기";
            closeBtn.addEventListener("click", closeModal);
        
            modalElem.appendChild(modalContentElem)
            modalElem.appendChild(closeBtn)
        
            modalWrapperElem.appendChild(modalElem)
            bodyElem.appendChild(modalWrapperElem)
        }

        const init = () => {
            const openModalBtnElem = document.querySelector('.open-modal-btn');
            openModalBtnElem.addEventListener('click', openModal);
        }

        document.addEventListener('road', () => {
            init();
        })
    </script>

</body>
</html>

 

 

[ 참고 자료 ]

 

Event.stopPropagation() - Web API | MDN

이벤트 캡쳐링과 버블링에 있어 현재 이벤트 이후의 전파를 막습니다.

developer.mozilla.org

 

버블링과 캡처링

 

ko.javascript.info

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함