stopPropagation vs stopImmediatePropagation

October 11, 2021

0. 작성이유

  • 누군가 물어봤는데.. 대답을 잘하지 못했다.

  • stopImmediatePropagation의 역할에 대해서 궁금했다.

  • 예제 HTML

    <div id="one">
        <div id="two">
            <div id="three">AA</div>
            <a id="four" href="google.com"> BB </a>
        </div>
    </div>

1. Event

  • DOM Event는 구독방식으로 작동한다.

    • 한 노드에 여러가지 이벤트를 등록 가능
    • 같은 Event 타입으로 여러가지의 핸들러를 등록 가능
    • 핸들러 작동 순서는 렉시컬 컨텍스트 순으로 작동된다.
  • 이벤트 등록 방식은 on[eventTyp], addEventListener(eventType, callback)

    • on방식은 이벤트가 쉐도잉 처리 되기때문에, 마지막에 할당한 핸들러만 작동

      • 쉐도잉은 on으로 등록된 이벤트만 처리되고, addEventListener는 정상 작동
    • addEventListener방식은 같은 이벤트에 여러 핸들러를 등록할 수 있다.

      • 해당 이벤트가 dispatch 되면, 등록된 핸들러가 다 작동된다.
      $four.addEventListener(
          'click',
          (event) => {
              console.log('four addEventListener1')
          },
          false
      )
      
      $four.addEventListener(
          'click',
          (event) => {
              console.log('four addEventListener2')
          },
          false
      )
      
      $four.onclick = (event) => {
          console.log('four onclick1')
      }
      
      $four.onclick = (event) => {
          event.preventDefault()
          console.log('four onclick2')
      }
      
      // four addEventListener1
      // four addEventListener2
      // four onclick2
  • 모던 브라우저의 기본 이벤트 타입은 버블링이다

    • 이벤트 리스너에서 처리하는게 버블링일뿐, 이벤트는 캡쳐링 → 버블링

    • addEventListner의 세번째 인자에 true로 바꾸면 캡쳐링

      $one.addEventListener(
          'click',
          (event) => {
              console.log('one')
          },
          true
      )
      $two.addEventListener(
          'click',
          (event) => {
              console.log('two')
          },
          false
      )
      $three.addEventListener(
          'click',
          (event) => {
              console.log('three')
          },
          false
      )
      
      // one
      // three
      // two

2. Event.preventDefault();

  • (<a>, <form>) 태그의 기본 이벤트(브라우저에 정의된)를 막는다.

    • <a id='four'> 기본 이벤트는 href에 등록된 주소로 페이지 이동
    • preventDefault 때문에 기본 기능을 하지 못하고 콘솔이 찍힌다.
    $four.addEventListener(
        'click',
        (event) => {
            event.preventDefault()
            console.log('four')
        },
        false
    )
    
    // four (not move to google.com)

3. Event.stopPropagation()

  • 브라우저의 이벤트 방식은 캡쳐링 → 버블링으로 이벤트 전파된다

    • document → body → target → body

      document.addEventListener(
          'click',
          (e) => {
              console.log('document')
          },
          true
      )
      
      document.addEventListener(
          'click',
          (e) => {
              console.log('document')
          },
          false
      )
      
      document.body.addEventListener(
          'click',
          (e) => {
              console.log('body')
          },
          true
      )
      
      document.body.addEventListener(
          'click',
          (e) => {
              console.log('body')
          },
          false
      )
      
      $one.addEventListener(
          'click',
          (event) => {
              console.log('one')
          },
          true
      )
      
      $one.addEventListener(
          'click',
          (event) => {
              console.log('one')
          },
          false
      )
      
      // document -> body -> target -> body -> document
  • 이러한 이벤트 전파 흐름을 막아준다

    • 캡쳐링 / 버블링 관계없이 이벤트의 전파 흐름을 막는다.

      document.addEventListener(
          'click',
          (e) => {
              console.log('document')
              e.stopPropagation()
          },
          true
      )
      
      document.body.addEventListener(
          'click',
          (e) => {
              console.log('body')
          },
          false
      )
      
      $one.addEventListener(
          'click',
          (event) => {
              console.log('one')
          },
          true
      )
      
      $one.addEventListener(
          'click',
          (event) => {
              console.log('one')
          },
          false
      )
      
      // document(더 이상 이벤트가 발생하지 않는다)
    • 단, 해당 노드에 등록된 이벤트 핸들러들은 작동된다.

      document.addEventListener(
          'click',
          (e) => {
              console.log('document1')
              e.stopPropagation()
          },
          true
      )
      
      document.addEventListener(
          'click',
          (e) => {
              console.log('document2')
          },
          true
      )
      
      document.body.addEventListener(
          'click',
          (e) => {
              console.log('body')
          },
          false
      )
      
      $one.addEventListener(
          'click',
          (event) => {
              console.log('one')
          },
          true
      )
      
      $one.addEventListener(
          'click',
          (event) => {
              console.log('one')
          },
          false
      )
      
      // document1
      // document2 (더 이상 이벤트가 발생하지 않는다)

4. Event.stopImmediatePropagation();

  • 해당 요소에 동일한 이벤트타입으로 등록된 핸들러의 전파를 멈춘다

    • 이벤트 핸들러는 등록 순서대로 작동된다(렉시컬 컨텍스트).

      $three.addEventListener(
          'click',
          (event) => {
              console.log('three1')
          },
          false
      )
      
      $three.addEventListener(
          'click',
          (event) => {
              console.log('three2')
          },
          false
      )
      
      // three1
      // three2
    • 순서 document → target(handler1, handler2 ..) → document순

      $three.addEventListener(
          'click',
          (event) => {
              console.log('three1')
          },
          false
      )
      
      $three.addEventListener(
          'click',
          (event) => {
              console.log('three2')
              event.stopImmediatePropagation()
          },
          false
      )
      
      $three.addEventListener(
          'click',
          (event) => {
              console.log('three3')
          },
          false
      )
      
      // three1
      // three2
      
      $three.addEventListener(
          'click',
          (event) => {
              console.log('three1')
              event.stopImmediatePropagation()
          },
          false
      )
      
      $three.addEventListener(
          'click',
          (event) => {
              console.log('three2')
          },
          false
      )
      
      $three.addEventListener(
          'click',
          (event) => {
              console.log('three3')
          },
          false
      )
      
      // three1

5. 특정 노드에 이벤트를 리셋시켜주고 싶을때는?

  • 관련 API는 찾을 수 없었고, 새롭게 노드를 만들어주는 방법뿐이다.
    elClone = $three.cloneNode(true)
    
    $three.parentNode.replaceChild(elClone, $three)

참고자료


© 2023, Customized by Joon