FrontEnd

[JS] Bubbling, Capturing, Event Propagation, 이벤트 전파

KJihun 2025. 4. 1. 14:17
728x90

 

Bubbling, Capturing

DOM(Document Object Model) 요소에 이벤트 발생 시, 그 이벤트가 어떻게 전달되는지를 정의하는 방식

 

이벤트 전파(Event Propagation)

브라우저에서 이벤트 발생 시, 이벤트는 DOM 트리 상에서 여러 단계에 걸쳐 전파된다.

  1. Capturing Phase (캡처링 단계)
    이벤트가 상위 요소에서 하위 요소로 내려오면서 전파
  2. Target Phase (타깃 단계)
    이벤트가 실제로 발생한 요소(target) 에 도달
  3. Bubbling Phase (버블링 단계)
    이벤트가 타깃 요소에서 다시 상위 요소로 전파
이벤트가 발생하면 해당 이벤트는 자식 요소에서 부모 요소로 전파되는 '버블링(bubbling)' 이 일어나게 된다.
버블링 전파 시, 부모 요소에 등록된 이벤트 리스너가 실행될 수 있다. 
JS는 기본적으로 버블링이 발생하기 때문에, 의도치 않은 이벤트 실행을 막으려면 버블링을 중지해야 한다

이 때 이벤트 전파를 중단하는 event.stopPropagation() 을 사용하여야 한다

 

<!-- 예시 코드-->
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>Event Stop Propagation 예제 1</title>
  </head>
  <body>
    <div id="parent" style="padding: 20px; background-color: lightblue;">
      부모 요소
      <button id="child">자식 버튼</button>
    </div>

    <script>
      // 부모 요소에 클릭 이벤트 리스너 등록
      document.getElementById("parent").addEventListener("click", function() {
        alert("부모 요소 클릭됨");
      });

      // 자식 버튼에 클릭 이벤트 리스너 등록
      document.getElementById("child").addEventListener("click", function(event) {
        event.stopPropagation(); // 부모 요소로 이벤트 전파 차단
        alert("자식 버튼 클릭됨");
      });
    </script>
  </body>
</html>

 

 

event.preventDefault()

이벤트가 발생했을 때 브라우저가 기본적으로 수행하는 동작을 취소하는 메서드

기본 이벤트를 지우고, 새로운 이벤트를 추가할 경우 주로 사용한다

 

1. 로그인 폼 처리 (submit 막고 Ajax 요청 보내기)

<form id="loginForm">
  <input type="text" name="username" placeholder="아이디" required />
  <input type="password" name="password" placeholder="비밀번호" required />
  <button type="submit">로그인</button>
</form>

<script>
document.getElementById("loginForm").addEventListener("submit", async function(e) {
  e.preventDefault(); // 기본 폼 제출 막기

  const formData = new FormData(this);
  const data = {
    username: formData.get("username"),
    password: formData.get("password")
  };

  try {
    const response = await fetch("/api/login", {
      method: "POST",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify(data)
    });

    if (response.ok) {
      alert("로그인 성공!");
      // location.href = "/dashboard"; // 리다이렉트 가능
    } else {
      alert("로그인 실패. 다시 시도하세요.");
    }
  } catch (err) {
    console.error(err);
    alert("서버 오류 발생");
  }
});
</script>

 

2. 댓글 입력 후 페이지 새로고침 없이 동적으로 추가하기

<form id="commentForm">
  <input type="text" name="comment" placeholder="댓글을 입력하세요" required />
  <button type="submit">등록</button>
</form>
<ul id="commentList"></ul>

<script>
document.getElementById("commentForm").addEventListener("submit", function(e) {
  e.preventDefault(); // 새로고침 방지

  const commentInput = this.elements["comment"];
  const comment = commentInput.value;

  if (!comment.trim()) return;

  const li = document.createElement("li");
  li.textContent = comment;
  document.getElementById("commentList").appendChild(li);

  commentInput.value = "";
});
</script>
 
 

 

3. 모바일에서 터치 스크롤 막기

document.addEventListener("touchmove", function(e) {
  if (shouldPreventScroll()) {
    e.preventDefault(); // 스크롤 방지
  }
}, { passive: false }); // passive: false 꼭 넣어야 preventDefault 작동함

function shouldPreventScroll() {
  return true; // 예: 특정 모달이 열렸을 때만 true 반환
}
 

 

4. 드래그 앤 드롭으로 이미지 업로드 처리

<div id="dropArea" style="width:300px;height:150px;border:2px dashed gray;">파일을 여기로 드래그하세요</div>

<script>
const dropArea = document.getElementById("dropArea");

["dragenter", "dragover", "dragleave", "drop"].forEach(eventName => {
  dropArea.addEventListener(eventName, e => {
    e.preventDefault(); // 브라우저 기본 동작 방지
    e.stopPropagation(); // 이벤트 버블링 중단
  });
});

dropArea.addEventListener("drop", e => {
  const files = e.dataTransfer.files;
  if (files.length > 0) {
    console.log("업로드할 파일:", files[0].name);
    // 파일 업로드 로직 추가
  }
});
</script>