프론트엔드 개발/JavaScript

자바스크립트) 이벤트

Ella Seon 2023. 7. 12. 14:44

0. 이벤트

- 이벤트는 어떤 사건을 의미함

- 즉, 사용자가 '클릭' 했을 때, '스크롤'을 내렸을 때, 무언가 '입력' 했을 때 등으로 상호작용으로 인해 일어나는 사건을 의미한다.

- DOM 요소와 관련이 있다.

1. 이벤트 종류

- 이벤트 종류는 아래 블로그를 참고하자

https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%F0%9F%92%AF-%EC%B4%9D-%EC%A0%95%EB%A6%AC

 

🌐 브라우저 이벤트 종류 & 사용법 총정리

브라우저 이벤트 란? 이벤트(event)는 어떤 사건을 의미한다. 브라우저에서의 이벤트란 예를 들어 사용자가 버튼을 클릭했을 때, 웹페이지가 로드되었을 때와 같은 것인데 이것은 DOM 요소와 관련

inpa.tistory.com

2. 이벤트 전파

- 이벤트 전파 : DOM  요소 노드에서 발생한 이벤트는 DOM 트리를 통해 전파된다. 

사용자의 다양한 입력을 통해 동적으로 생성되는 이벤트 객체는 이벤트를 발생시킨 타깃을 중심으로 DOM 트리를 통해 전파된다. 

 

전파되는 방향에 따라 1) 캡처링 2) 타깃 3) 버블링 으로 구분할 수 있다.

 

1) 캡처링 단계(실제 코드에서 자주 쓰이지는 않는다)

- 이벤트 전파의 첫 번째 단계
- 하위 엘리먼트에 이벤트 핸들러가 있을 때, 상위 엘리먼트로부터 이벤트가 발생하기 시작해서 하위 엘리먼트까지 이벤트가 전달되는 방식을 말한다. `(상 → 하)`

 

🔸캡처링 예제코드

<div id="outer">
  <div id="inner">
    <button id="btn">Click me</button>
  </div>
</div>

<script>
  document.getElementById("outer").addEventListener("click", function(event) {
    console.log("Outer div clicked during capturing");
  }, true); // true로 설정하여 캡처링 단계에서 이벤트 처리

  document.getElementById("inner").addEventListener("click", function(event) {
    console.log("Inner div clicked during capturing");
  }, true);

  document.getElementById("btn").addEventListener("click", function(event) {
    console.log("Button clicked during capturing");
  }, true);
</script>
[실행결과]
Outer div clicked during capturing
Inner div clicked during capturing
Button clicked during capturing

- 위의 예제에는 세개의 요소가 중첩되어있다. 

버튼을 클릭하면 "Button clicked during capturing"이 먼저 출력되고, 그 다음 "Inner div clicked during capturing"이 출력되며, 마지막으로 "Outer div clicked during capturing"이 출력된다. 이는 이벤트 캡처링 단계에서 이벤트가 가장 바깥쪽 요소에서 가장 안쪽 요소로 전파되기 때문이다. addEventListener의 세 번째 매개변수를 true로 설정하여 캡처링 단계에서 이벤트를 처리하도록 지정했다.

 

2) 타깃 단계

- 이벤트가 이벤트 타깃에 도달

 

3) 버블링 단계

-  하위 엘리먼트에 이벤트가 발생할 때 그 엘리먼트로부터 시작해서 상위 요소까지 이벤트가 전달되는 방식을 말한다. `(하 → 상)`

- 즉, 한 요소에 이벤트가 발생되면, 그 요소의 부모 요소의 이벤트도 같이 발생되는 이벤트 전파

- 자바스크립트 코드에서 기본 동작은 '이벤트 버블링' 이다. 

 

🔸이벤트 버블링 예제 코드

<div id="outer">
  <div id="inner">
    <button id="btn">Click me</button>
  </div>
</div>

<script>
  document.getElementById("outer").addEventListener("click", function(event) {
    console.log("Outer div clicked");
  });

  document.getElementById("inner").addEventListener("click", function(event) {
    console.log("Inner div clicked");
  });

  document.getElementById("btn").addEventListener("click", function(event) {
    console.log("Button clicked");
  });
</script>
[실행결과]

Button clicked
Inner div clicked
Outer div clicked

위 예제코드에서 세개의 요소가 중첩되어있다. 버튼만 클릭했는데도 불구하고 버튼 바깥의 div 태그가 모두 실행되는 걸 볼수 있다. 즉, 이벤트가 버튼에서 시작해서 상위요소로 전파된다. 

 

🔸이벤트 캡처링, 타깃, 버블링 예제 그림

- 위 그림을 보면, span 태그를 클릭하면 이벤트가 최상위 조상에서 시작해 아래로 전파되고(캡처링 단계), 이벤트가 타깃 요소(span) 에 도착해 실행된 후 (타깃단계) 다시 위로 전파된다(버블링단계). 이런 과정을 통해 요소에 할당된 이벤트 핸들러가 호출된다. 

- 캡처링 단계를 이용해야하는 경우는 흔치 않다. 

 

🔸이벤트 전파가 있는 이유

가만 생각해보면 이벤트 전파는 어쩌면 당연한 현상일지도 모른다.

자식 요소가 부모 요소 영역 안에 위치하고 있기 때문자식 요소만을 클릭하였다 하더라도 다른 시각으로 보면 부모 요소도 클릭한 셈이 되기 때문이다.
만일 위의 그림에서 타겟인 button 영역을 클릭해도 div영역 안에 있기 때문에 논리적으로 div 영역의 이벤트도 같이 발생하는 것이 옳을 것이다.

3. 이벤트 버블링 문제점(e.stopPropagation())

- 부모와 자식 둘다 이벤트를 등록한 상태에서, 자식 요소만 클릭했을때만 이벤트를 발생하고 부모 요소는 이벤트를 발생시키고 싶지 않은 상황이 있을 것이다. 

- 하지만 브라우저는 기본적으로 캡처링 - 버블링으로 동작 되기 때문에 이벤트 동작 자체를 바꿀순 없다. 그리고 html 구조를 봐도  자식 요소가 부모 요소 영역 안에 위치하고 있기 때문에 자식 요소만을 클릭하였다 하더라도 다른 시각으로 보면 부모 요소도 클릭한 셈이 되어 논리적으로 구조적으로 이것이 옳다.
따라서 브라우저의 이벤트 구조를 바꿀수는 없고, 엘리먼트의 이벤트 전파를 방지 처리 하는 식으로 해결하여야 한다.

 

🔸e.stopPropagation() 을 사용한 예제코드

<div id="outer">
  <div id="inner">
    <button id="btn">Click me</button>
  </div>
</div>

<script>
  document.getElementById("outer").addEventListener("click", function(event) {
    console.log("Outer div clicked");
  });

  document.getElementById("inner").addEventListener("click", function(event) {
    console.log("Inner div clicked");
    event.stopPropagation(); // 이벤트 전파 중지
  });

  document.getElementById("btn").addEventListener("click", function(event) {
    console.log("Button clicked");
    event.stopPropagation(); // 이벤트 전파 중지
  });
</script>
[실행 결과]
Button clicked

- 위의 예제에서는 inner div와 버튼에 이벤트 핸들러를 등록하고, 해당 핸들러에서 event.stopPropagation()을 호출하여 이벤트 전파를 중지시킨다.

- 버튼을 클릭하면 "Button clicked"만 출력된다. inner div와 outer div의 클릭 이벤트는 발생하지 않는다. 이는 inner div와 버튼의 클릭 이벤트 핸들러에서 event.stopPropagation()을 호출하여 이벤트 전파가 중지 되었기 때문이다. 이를 통해 버블링 단계에서 상위 요소로 이벤트 전파되는 문제를 해결할 수 있다.

4. 이벤트 위임

- 이벤트 위임 : 여러 요소의 이벤트 핸들러를 할당하지 않고, 하나의 상위 요소에 이벤트 핸들러를 등록하여 하위 요소들의 이벤트를 처리하는 방법

이벤트 전파 개념이 없다면 <button> 태그마다 이벤트를 등록해야하는 노가다를 펼쳐야 할지도 모른다. 하지만, 버블링의 특성을 통해 부모 요소에만 이벤트를 등록하면, 어떤 몇번째의 button 태그를 클릭하든 부모 요소로 이벤트가 전파되기 때문에 원하는 구현을 간단하게 할 수 있다.

 

공통 조상에 할당한 핸들러에서 event.target을 이용하면 실제 어디서 이벤트가 발생했는지 알 수 있습니다. 이를 이용해 이벤트를 핸들링한다.

 

※event.target : 클릭이벤트가 발생한 HTML 요소

<div id="container">
  <button id="btn1">Button 1</button>
  <button id="btn2">Button 2</button>
  <button id="btn3">Button 3</button>
</div>

<script>
  document.getElementById("container").addEventListener("click", function(event) {
    if (event.target.tagName === 'BUTTON') {
      console.log("Clicked button:", event.target.textContent);
    }
  });
</script>
[실행결과]

Clicked button: Button 2

위의 예제에서는 <div id="container"> 요소에 이벤트 핸들러를 등록하고, 클릭 이벤트가 발생했을 때 이벤트의 타겟 요소가 <button> 요소인지 확인하여 처리한다. 모든 버튼에 대한 클릭 이벤트를 하나의 이벤트 핸들러에서 처리할 수 있다. 

이벤트 위임을 사용하면 요소 갯수가 많거나 동적으로 생성되는 경우에 유용하다.

5. e.preventDefault

이벤트의 기본동작을 취소하는 메서드이다. 이 메서드를 호출하면 브라우저가 해당 이벤트에 대해 수행하는 기본 동작을 중단시킬 수 있다. 주로 폼 제출, 링크 클릭 등의 이벤트에서 사용된다. 

<a href="https://www.example.com" id="myLink">Click me</a>

<script>
  document.getElementById("myLink").addEventListener("click", function(event) {
    event.preventDefault(); // 링크 클릭에 대한 기본 동작 중단
    console.log("Link clicked, but default action prevented");
  });
</script>

위의 예제에서는 <a> 요소에 클릭 이벤트 핸들러를 등록하고, 핸들러 내에서 event.preventDefault()를 호출하여 링크 클릭에 대한 기본 동작을 막는다. 대신 콘솔에 "Link clicked, but default action prevented"를 출력한다.

 

[실행 결과]
Link clicked, but default action prevented

링크를 클릭하면 기본적으로 페이지가 해당 링크로 이동하지만, 위의 예제에서는 event.preventDefault()가 호출되어 링크 클릭에 의한 페이지 이동이 막히고, 대신 콘솔에 메시지가 출력된다. 

 

<html>
  <body>
    <a href="https://www.google.com">go</a>
    <input type="checkbox" />
    <script>
      document.querySelector("a").onclick = (e) => {
        // a 요소의 기본 동작을 중단한다. 페이지 이동 못함
        e.preventDefault();
      };

      document.querySelector("input[type=checkbox]").onclick = (e) => {
        // checkbox 요소의 기본 동작을 중단한다. 체크박스에 체크 표시 못함
        e.preventDefault();
      };
    </script>
  </body>
</html>

참고자료

https://www.youtube.com/watch?v=0jtalJxrxhs

https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-%EB%B2%84%EB%B8%94%EB%A7%81-%EC%BA%A1%EC%B3%90%EB%A7%81

 

🌐 한눈에 이해하는 이벤트 흐름 제어 (버블링 & 캡처링)

HTML 이벤트의 흐름 HTML 문서의 각 엘리먼트들은 아래와 같이 태그 안의 태그가 위치하는 식으로 계층적으로 이루어짐을 볼 수 있다. 이러한 계층적 구조 특징 때문에 만일 HTML 요소에 이벤트가

inpa.tistory.com

https://ingg.dev/event-delegation/

 

[JS] 이벤트 전파와 이벤트 위임 이해하기

이벤트 버블링과 캡쳐링, 그리고 이벤트 위임은 무엇인지 구현해보며 이해해보자.

ingg.dev