이벤트

Created
April 20, 2024
Tags
JavaScript

JavaScript에서 이벤트(event)는 웹 페이지에서 사용자 상호작용, 브라우저 조작 등 여러 상황에서 발생하는 행위를 의미한다. 이벤트는 DOM 요소의 상태 변화나 사용자 입력에 의해 트리거되며, 이를 통해 JavaScript 코드가 특정 동작을 수행하거나 반응할 수 있다. 이벤트 처리는 웹 애플리케이션의 동적인 동작을 구현하는 핵심 요소 중 하나이다.

쉽게 말해 이벤트란 유저가 브라우저에서 클릭, 마우스 오버, 스크롤, 포커스, 드래그 등의 조작을 가하는 행위이다. 만약, 유저가 어떤 버튼을 클릭했을 때 버튼의 색상을 바꾸고 싶다면? 색상을 바꾸는 작업을 수행하는 것이 이벤트 리스너이다.

이벤트 리스너

image

정의

이벤트 리스너(Event Listener)는 특정 이벤트가 발생했을 때 실행될 함수를 정의하고 이를 이벤트 대상에 등록하는 방법을 말한다.

이벤트 리스너 등록

addEventListener 함수는 특정 이벤트가 발생했을 때 실행될 이벤트 리스너를 등록하는 데 사용된다.

element.addEventListener(eventType, listener, options);
  • eventType: 문자열로, 감지할 이벤트의 유형을 지정한다 (예: click, scroll, keydown).
  • listener: 이벤트가 발생했을 때 실행될 함수.
  • options (선택적): 객체로, 이벤트 리스너에 대한 추가 옵션을 지정한다. 예를 들어, { capture: true }는 캡처링 단계에서 이벤트를 감지하도록 한다.

예제: 버튼 클릭 시 색상 변경

<!DOCTYPE html>
<html>
<head>
    <title>Event Listener Example</title>
    <style>
        #myButton {
            padding: 10px 20px;
            background-color: blue;
            color: white;
            border: none;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <button id="myButton">Click me!</button>

    <script>
        // 이벤트 타겟 요소 선택
        const button = document.getElementById('myButton');

        // 이벤트 리스너 등록
        button.addEventListener('click', function() {
            // 버튼의 배경색을 빨간색으로 변경
            button.style.backgroundColor = 'red';
        });
    </script>
</body>
</html>

이벤트 리스너 제거

removeEventListener 함수는 등록된 이벤트 리스너를 제거하는 데 사용된다. 이 함수는 addEventListener와 동일한 인수를 받습니다. 단, 제거할 이벤트 리스너는 정확히 동일한 참조를 가져야 합니다.

element.addEventListener(eventType, listener, options);
  • eventType: 문자열로, 감지할 이벤트의 유형을 지정한다 (예: click, scroll, keydown).
  • listener: 이벤트가 발생했을 때 실행될 함수.
  • options (선택적): 객체로, 이벤트 리스너에 대한 추가 옵션을 지정한다. 예를 들어, { capture: true }는 캡처링 단계에서 이벤트를 감지하도록 한다.

이벤트 객체(Event Object)

이벤트 리스너가 실행될 때, 브라우저는 이벤트 객체를 핸들러 함수에 전달한다. 이벤트 객체에는 이벤트에 대한 정보가 담겨 있다.

button.addEventListener('click', function(event) {
    console.log('Event type:', event.type);  // "click"
    console.log('Event target:', event.target);  // 클릭된 요소
});

위 예제에서는 이벤트 객체를 사용하여 이벤트 타입과 이벤트가 발생한 요소에 대한 정보를 출력하고 있다.

이벤트 전파(Event Propagation)

이벤트가 DOM 내에서 전파되는 방식에는 세 가지가 있다. 각 내용은 아래에서 더 자세하게 설명하고 있다.

  • 캡처링 단계(Capturing Phase): 이벤트가 최상위 요소에서 시작하여 하위 요소로 이동하는 단계입니다.
  • 타겟 단계(Target Phase): 이벤트가 실제 대상 요소에 도달하여 이벤트 핸들러가 호출되는 단계입니다.
  • 버블링 단계(Bubbling Phase): 이벤트가 대상 요소에서 시작하여 상위 요소로 이동하는 단계입니다.

이벤트 위임(Event Delegation)

이벤트 위임은 많은 자식 요소에게 개별적으로 이벤트 리스너를 등록하는 대신, 공통의 상위 요소에 이벤트 리스너를 등록하여 이벤트를 처리하는 기법이다. 이는 성능을 향상시키고, 동적으로 생성된 요소에도 이벤트를 적용할 수 있게 한다.

예제: 이벤트 아이템 클릭 시 처리

<!DOCTYPE html>
<html>
<head>
    <title>Event Delegation Example</title>
</head>
<body>
    <ul id="myList">
        <li>Item 1</li>
        <li>Item 2</li>
        <li>Item 3</li>
    </ul>

    <script>
        const list = document.getElementById('myList');

        list.addEventListener('click', function(event) {
            if (event.target && event.target.nodeName === 'LI') {
                alert('List item clicked: ' + event.target.textContent);
            }
        });
    </script>
</body>
</html>

위 예제에서는 myList라는 id를 가진 리스트 요소에 클릭 이벤트 리스너를 등록하고, 클릭된 요소가 리스트 아이템(LI)일 때만 동작하도록 했습니다. 이는 동적으로 추가된 리스트 아이템에도 동일하게 이벤트를 처리할 수 있게 합니다.

이벤트 관련 함수들

아래 예제는 이벤트 버블링 현상을 이해하기 위한 예제다.

<div class="outer">
    Outer Container
    <div class="inner">
        Inner Container
        <button id="myButton">Click Me!</button>
    </div>
</div>

<script>
    // 이벤트 리스너 등록
    const outer = document.querySelector('.outer');
    const inner = document.querySelector('.inner');
    const button = document.getElementById('myButton');

    outer.addEventListener('click', function() {
        console.log('Outer container clicked!');
    });

    inner.addEventListener('click', function() {
        console.log('Inner container clicked!');
    });

    button.addEventListener('click', function() {
        console.log('Button clicked!');
    });
</script>

위 예제에서는 outer 요소와 inner 요소, 그리고 button 요소에 각각 클릭 이벤트 리스너를 등록했다. 버튼을 클릭했을 때 button 요소에 등록된 클릭 이벤트 리스너가 실행될거라고 생각했다. 하지만, 이벤트 버블링으로 인해 상위 요소(innerouter)에 등록된 이벤트 리스너도 실행되었다.

Button clicked!
Inner container clicked!
Outer container clicked!

이벤트 버블링

이벤트 버블링은 이벤트가 발생했을 때, 해당 요소부터 시작해 부모 요소들을 따라 점점 위로 전파되는 방식이다. 최종적으로 window 객체까지 전파된다. 이런 흐름을 이벤트 버블링히라고 하며, 점점 커지는 모양이 물 속 거품(Bubble 🫧)과 닮았기 때문이라고 한다.

💡
거의 모든 이벤트는 버블링이 된다. focus 이벤트와 같이 버블링 되지 않는 이벤트도 있다.

이벤트 버블링 중단하기

이벤트 버블링을 중단하려면, 이벤트 리스너 내에서 event.stopPropagation() 메서드를 호출하면 된다. 이 메서드는 이벤트가 상위 요소로 전파되지 않도록 한다.

button.addEventListener('click', function(event) {
	 event.stopPropagation();	
	 console.log('Button clicked!');
});

이벤트 버블링은 타킷 이벤트에서 시작해서 <html> 요소를 고쳐 document 객체를 만날 때까지 각 노드에서 모두 발생한다. 몇몇 이벤트는 window 객체까지 거슬러 올라가기도 한다. 이 때도 모든 핸들러가 호출된다.

event.target

event.target은 이벤트 객체에서 사용되는 속성으로, 이벤트가 발생한 실제 DOM 요소를 가리킨다. 예를 들어, 클릭 이벤트가 발생했을 때 사용자가 클릭한 요소가 event.target이 되는 것이다. 이때, event.targetevent.currentTarget(this)은 다음과 같은 차이가 있다.

  • event.target은 이벤트가 시작된 위치(태그)를 의미한다. 버블링이 진행되어도 변하지 않는다.
  • event.currentTarget 은 이벤트 핸들러와 연결된 위치(태그)를 의미한다. 현재 실행 중인 핸들러가 할당된 요소를 참조한다.

event.preventDefault

event.preventDefault 함수는 이벤트의 기본 동작을 막는 데 사용된다.

  • 폼을 제출할 때, JavaScript로 유효성 검사를 수행하고 문제가 있을 경우 폼이 제출되지 않게 할 수 있다.
  • 특정 상황에서 링크가 작동하지 않도록 해야 할 때, event.preventDefault()를 사용하여 링크 클릭을 방지할 수 있다.
  • 드래그 앤 드롭 동작을 방지하여 사용자가 특정 요소를 드래그하지 못하게 할 수 있다.
  • 특정 키 입력을 방지하여 사용자가 원하지 않는 동작을 수행하지 못하도록 할 수 있다.
const link = document.getElementById('myLink');

link.addEventListener('click', function(event) {
    event.preventDefault();
    alert('Link click prevented!');
});

즉, 어떤 이벤트를 명시적으로 처리하지 않은 경우, 해당 이벤트에 대한 기본 동작을 실행하지 않도록 지정한다. 아래 링크에서는 event.preventDefault 를 통해 <checkbox> 의 기본 동작을 막는 모습을 볼 수 있다.

https://developer.mozilla.org/ko/docs/Web/API/Event/preventDefault

event.stopImmediatePropagation

event.stopImmediatePropagation 함수는 현재 요소에서의 다른 이벤트 리스너들이 호출되지 않도록 막는다. 이는 동일한 이벤트에 등록된 다른 리스너들의 실행을 방지한다. 즉, 버블링을 포함해서 현재 레벨에 걸린 다른 이벤트의 동작도 막는다.

dataset

HTML 요소에 데이터 속성을 추가하고 접근하는 방법을 제공하는 속성이다. dataset 속성은 HTML5에서 도입된 data-* 속성을 통해 데이터 속성을 사용할 수 있게 해준다. 이를 통해 JavaScript에서 이 데이터에 접근할 수 있다.

사용법

  1. 태그에 접두어 data- 가 붙은 속성을 추가하고, 사용하고자 하는 값을 지정한다.
  2. 자바스크립트에서 요소를 선택하고 dataset 객체에서 커스텀 속성을 읽어들인다. 이 때, 접두어는 생략.
<img src="thumb1.jpg" class="thumbnail" data-large-url="large1.jpg">
<img src="thumb2.jpg" class="thumbnail" data-large-url="large2.jpg">
<img src="thumb3.jpg" class="thumbnail" data-large-url="large3.jpg">

<img id="largeImage" class="large-image" src="" alt="Large Image">

<script>
    const thumbnails = document.querySelectorAll('.thumbnail');
    const largeImage = document.getElementById('largeImage');

    thumbnails.forEach(thumbnail => {
        thumbnail.addEventListener('click', function() {
            const largeUrl = thumbnail.dataset.largeUrl;
            largeImage.src = largeUrl;
        });
    });
</script>

위 예제는 이미지 갤러리에서 썸네일을 클릭할 때, 큰 이미지를 표시하는 예제이다. 썸네일에 data-* 속성을 추가하여 큰 이미지 URL을 저장한다.

이벤트 캡처링

이벤트 캡처링은 이벤트 버블링과 반대 방향으로, 이벤트가 상위 요소에서 하위 요소로 전파된다. 즉, 이벤트가 루트 요소에서 시작하여 점점 깊은 자식 요소로 전파된다. 이벤트 리스너를 캡처링 단계에서 실행되도록 하려면 addEventListener의 세 번째 인수로 capture 옵션을 true를 전달한다. capture 옵션은 두 가지 값을 가질 수 있다.

  • false(default): 핸들러는 버블링 단계에서 동작한다.
  • true: 핸들러는 캡처링 단계에서 동작한다.

스크롤 이벤트

스크롤 이벤트는 사용자가 웹 페이지에서 스크롤을 할 때 동작을 감지하는 이벤트이다. 브라우저는 사용자가 스크롤을 할 때마다 window, document 또는 특정 요소에서 스크롤 이벤트를 발생시키며, 이를 통해 우리는 특정 동작을 수행하도록 스크립트를 작성할 수 있다. 스크롤 이벤트는 주로 JavaScript에서 scroll 이벤트 리스너를 통해 처리된다.

스크롤 이벤트 리스너

가장 간단한 스크롤 이벤트 리스너는 window 객체에 등록하여 사용자가 페이지를 스크롤할 때마다 특정 함수를 호출하는 것이다.

window.addEventListener('scroll', function() {
	console.log('123');
}

주요 메서드

1. window.scrollTo()

페이지의 시작점(0, 0)을 기준으로 특정 좌표로 스크롤한다. 즉, 절대 좌표로 스크롤을 이동시킨다.

window.scrollTo(0, 500);

window.scrollTo({
    top: 500,
    left: 0,
    behavior: 'smooth' // smooth, auto
});
  • 첫 번째 예제에서는 페이지를 수직으로 500px 위치로 스크롤한다. 수평 스크롤은 0px로 설정된다.
  • 두 번째 예제에서는 scrollTo 메서드를 객체 형식으로 사용하여 스크롤의 애니메이션 동작을 지정할 수 있다.

2. window.scrollBy()

현재 스크롤 위치를 기준으로 상대적인 좌표로 스크롤한다. 즉, 현재 위치에서 특정 거리만큼 스크롤을 이동시킨다.

window.scrollBy(50, 100); 

window.scrollBy({
    top: 100,
    left: 50,
    behavior: 'smooth' // smooth, auto
});
  • 첫 번째 예제에서는 현재 스크롤 위치에서 수평으로 50px, 수직으로 100px만큼 이동한다.
  • 두 번째 예제에서는 scrollBy 메서드를 객체 형식으로 사용하여 스크롤의 애니메이션 동작을 지정할 수 있다.

주요 프로퍼티

1. window.scrollY()

문서의 수직 스크롤 위치를 반환한다. 즉, 현재 페이지의 맨 위에서부터 얼마나 스크롤 했는지 픽셀 수를 나타낸다.

2. window.scrollX()

문서의 수평 스크롤 위치를 반환한다. 즉, 현재 페이지의 맨 왼쪽에서부터 얼마나 스크롤 했는지 픽셀 수를 나타낸다.

3. document.documentElement.scrollTopdocument.documentElement.scrollLeft

문서 루트 요소의 스크롤 위치를 설정하거나 가져온다. 쉽게 말해 현재 페이지 스크롤 위치를 반환할 수 있고, 숫자를 입력하면 그만큼 페이지 강제 이동도 해준다.

var scrollTop = document.documentElement.scrollTop;
document.documentElement.scrollTop = 100;
var scrollLeft = document.documentElement.scrollLeft;
document.documentElement.scrollLeft = 50;

4. element.scrollHeight

요소의 전체 스크롤 가능한 높이를 반환한다.

5. element.scrollWidth

요소의 전체 스크롤 가능한 너비를 반환한다.

정리

  • 이벤트는 사용자 상호작용이나 브라우저 조작 시 발생하며, 웹 애플리케이션의 동적 동작을 구현하는 데 중요하다
  • 이벤트 버블링과 캡처링을 잘 이해하고 있으면, 이벤트 처리를 원하는 순서대로 제어할 수 있고 불필요한 이벤트 전파 또한 막아 복잡한 UI 동작을 효율적으로 구현할 수 있을 것이다.
  배열과 객체 👉🏻