사소한 삽질 - event bubbling

최재홍·2023년 5월 1일
0
post-custom-banner

리스트로 쌓이게 될 컴포넌트의 형태가 위와 같다. 그리고 컴포넌트 전체에 대해서는 onClick 이벤트 핸들러가 할당되어 있고, 그 내부의 빨간색 휴지통 모양 버튼에도 다른 onClick 이벤트 핸들러가 할당되어 있다.

전체 컴포넌트에는 클릭시에 특정 URL로 이동하는 함수가 할당되어 있고, 휴지통 모양 버튼에는 서버에 delete요청을 하는 함수가 할당되어 있었다.

시험삼아 휴지통 버튼을 클릭하자, 서버에서 해당 데이터가 삭제가 되면서 URL이동을 하였다. 당연히 서버에서 삭제된 데이터를 이동한 url의 route에서 읽어오려고 시도하고 있으니 에러 메시지가 발생하였다. 나는 삭제만 하고 싶었던건데.

이벤트 버블링

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

ex)

<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>에 할당된 핸들러가 동작한다.
  3. 그 바깥의 <form>에 할당된 핸들러가 동작한다.
  4. document 객체를 만날 때까지, 각 요소에 할당된 onclick 핸들러가 동작한다.

이런 흐름을 '이벤트 버블링'이라고 부른다.

이벤트 버블링을 방지하는 방법1

event.target과 this(=event.currentTarget)을 구분하여 함수를 할당하면 원하는 요소에만 함수를 실행시킬 수 있다.

  • event.target은 실제 이벤트가 시작된 '타깃'요소이다. 버블링이 진행되어도 변하지 않는다.
  • this는 '현재'요소로, 현재 실행중인 핸들러가 할당된 요소를 참조한다.

form.onclick = function(event) {
  event.target.style.backgroundColor = 'yellow';

  // chrome needs some time to paint yellow
  setTimeout(() => {
    alert("target = " + event.target.tagName + ", this=" + this.tagName);
    event.target.style.backgroundColor = ''
  }, 0);
};

form요소를 정확히 클릭했을 때 -> target = FORM, this = FORM
div요소를 정확히 클릭했을 때 -> target = DIV, this = FORM
p요소를 정확히 클릭했을 때 -> target = P, this = FORM

이벤트 버블링을 방지하는 방법2

event.stopPropagation();

이벤트 핸들러로 실행될 함수 내부에서 실행시켜주면 이벤트 버블링을 방지해준다.

ex)

<TodoWrapper
      onClick={() => {
        navigate(`/Detail/${id}`);
      }}
    >
      <div id='upper-div'>
        <div id='todo-title-text'>{title}</div>
        <button onClick={(event) => handleDelete(event, id)}>
          <DeleteButtonIcon />
        </button>
      </div>
      <div>
        <div>{name}</div>
      </div>
    </TodoWrapper>

const handleDelete = (event, id) => {
    event.stopPropagation();
    const result = window.confirm("정말로 삭제하시겠습니까?");
    result && mutation.mutate(id);
  };

이게 내가 겪었던 상황인데 event.stopPropagation() 부분이 없으면 이벤트 버블링 현상으로 삭제를 진행하고 나서 그 위 요소의 event핸들러로 할당되어 있는 navigate함수도 실행되어 버린다.

이렇게 실행시키고 싶은 함수 내부에 event.stopPropagation()를 실행시켜주면 이벤트 버블링을 방지할 수 있다.

post-custom-banner

0개의 댓글