[JavaScript] 이벤트의 버블링과 캡쳐링

SangHun Han·2023년 5월 17일
0
post-thumbnail

HTML 이벤트의 흐름

HTML 문서의 각 요소들은 아래와 같이 태그 안의 태그가 위치하는 식으로 계층적이라고 볼 수 있다.

<form onclick="alert('form')">
  FORM
    <div onclick="alert('div')">
      DIV
    	 <p onclick="alert('p')">
           P
         </p>
    </div>
</form>

예를 들어, 3개가 중첩된 영역에서 최하위 자식 요소인 p 태그를 클릭하면 onclick 이벤트 스크립트가 p 뿐만 아니라 그의 부모인 div와 form 요소들도 발생한다는 사실을 알 수 있다.

이러한 계층적 구조 특징 때문에 만일 HTML 요소에 이벤트가 발생할 경우 연쇄적 이벤트 흐름이 일어나게 된다.

이러한 현상을 이벤트 전파(Event Propagation)라고 부르며, 전파 방향에 따라 버블링과 캡쳐링으로 구분한다.



버블링과 캡쳐링

  • 버블링(Bubbling) : 자식 요소에서 발생한 이벤트가 바깥 부모 요소로 전파 (기본값)
  • 캡쳐링(Capturing) : 자식 요소에서 발생한 이벤트가 부모 요소부터 시작해서 안쪽의 자식 요소로 전파

버블링의 예시

<!DOCTYPE html>
<html lang="ko-KR">
    <head>
        <meta charset="UTF-8"/>
        <title>이벤트 - 버블링</title>
        <style>
            section {
          		width: 400px;
                height: 400px;
                background-color: green;
            }
            div {
                width: 300px;
                height: 300px;
                background-color: yellow;
            }

            p {
                width: 200px;
                height: 200px;
                background-color: red;
            }
        </style>
    </head>

    <section onclick="console.log('section : 버블링3')">
        <div onclick="console.log('div : 버블링2')">
            <p onclick="console.log('p : 버블링1')"></p>
        </div>
    </section>
</html>

버블링 이벤트 결과를 확인해보면 가장 먼저 빨간색 박스를 즉, 가장 안쪽 자식 요소인 p태그 부분을 클릭했을 때 위의 부모 요소까지 적용되어 모든 출력이 나타나고 있으며 그 다음 자식 요소인 div태그 부분인 노란색 박스를 클릭했을 때는 자기 자신과 바로 위의 부모 요소인 section태그 부분만 출력이 나타난다.

마지막으로 가장 바깥쪽의 최상단 부모 요소인 section태그 부분(초록색 박스)을 클릭했을 때는 section태그로 나타낸 출력 값만 출력이 된다.


캡쳐링의 예시

<!DOCTYPE html>
<html lang="ko-KR">
    <head>
        <meta charset="UTF-8" />
        <title>이벤트 - 캡쳐링</title>
        <style>
            section {
                width: 400px;
                height: 400px;
                background: green;
            }
            div {
                width: 300px;
                height: 300px;
                background: yellow;
            }

            p {
                width: 200px;
                height: 200px;
                background: red;
            }
        </style>
    </head>

    <section id="first">
        <div id="second">
            <p id="third"></p>
        </div>

        <script>
            first.addEventListener('click', (e) => {
                console.log('section : 캡쳐링1');
            }, true)
            second.addEventListener('click', (e) => {
                console.log('div : 캡쳐링2');
            }, true)
            third.addEventListener('click', (e) => {
                console.log('p : 캡쳐링3');
            }, true)
        </script>
    </section>
</html>

캡쳐링 이벤트 결과를 확인해보면, 버블링과 동일하게 순서대로 p태그(빨간색 박스), div태그(노란색 박스), section태그(초록색 박스)를 클릭할 때 마다 최상단의 부모 요소부터 가장 안쪽의 자식 요소 순서로 출력이 되는 것을 확인해 볼 수 있다.

각 이벤트에 "true"값을 붙여야 캡쳐링이 발생함



이벤트 전파 제어하기

❗ 버블링과 캡쳐링의 문제점

버블링과 캡쳐링을 사용할 때, 만약에 부모와 자식 둘다 이벤트를 등록한 상태에서
자식 요소를 클릭했을때만 이벤트를 발생하고 부모 요소는 이벤트를 발생시키고 싶지 않은 상황이 있을 것이다.

Element(요소)의 이벤트 전파 방지를 처리하자!


📌 stopPropagation()

stopPropagation() 메소드를 호출하면 버블링 또는 캡쳐링에 따라 상위, 하위로 가는 이벤트 전파를 막을 수 있다.

<!DOCTYPE html>
<html lang="ko-KR">
    <head>
        <meta charset="UTF-8" />
        <title>이벤트 - 캡쳐링</title>
        <style>
            section {
                width: 400px;
                height: 400px;
                background: green;
            }
            div {
                width: 300px;
                height: 300px;
                background: yellow;
            }

            p {
                width: 200px;
                height: 200px;
                background: red;
            }
        </style>
    </head>

    <section id="first">
        <div id="second">
            <p id="third"></p>
        </div>

        <script>
            first.addEventListener('click', (e) => {
          		e.stopPropagation() // 이벤트 전파 방지
                console.log('section');
            })
            second.addEventListener('click', (e) => {
          		e.stopPropagation() // 이벤트 전파 방지
                console.log('div');
            })
            third.addEventListener('click', (e) => {
          		e.stopPropagation() // 이벤트 전파 방지
                console.log('p');
            })
        </script>
    </section>
</html>

각 요소를 클릭했을 때, 가장 하위 자식 요소부터 최상위 부모 요소까지 순서대로 p, div, section이 "한번씩" 정상적으로 출력되는 모습을 볼 수 있다.



헷갈리지말자! 이벤트 전파를 막는다고 생각하지말자!🤨

📌 preventDefault()

preventDefault()는 기본적인 이벤트 동작 자체를 취소하지만, 이벤트 전파를 막지는 못한다.

<!-- 앵커의 기본 동작을 중지 -->
<a href="https://www.naver.com" class="link">네이버 링크입니다</a>
<script>
    const link = document.querySelector('.link');
    link.addEventListener('click', (e) => {
        console.log('clicked');
        e.preventDefault();
    })
</script>

<!-- submit 의 기본 동작을 중지 -->
<form action="">
    <button type="submit" class="submit">제출</button>
</form>
<script>
    const submit = document.querySelector('.submit');
    submit.addEventListener('click', (e) => {
        console.log('clicked');
        e.preventDefault();
    })
</script>

다른 예시로 들어볼때, "네이버 링크입니다"라는 제목의 "링크'와 submit 이벤트를 가진 "버튼"을 한번씩 클릭해본 결과는 preventDefault() 메소드로 인해 두개의 동작 모두 아무일도 일어나지 않고 console.log 출력값만 찍히는 현상을 볼 수 있다.


<a href="url">와 같은 링크 기능이나 <form>태그의 submit 이벤트를 취소할 때 유용하게 쓰인다.

profile
매일매일 성장하는 개발자 🚀

0개의 댓글