이벤트

se-een·2023년 2월 3일
0
post-thumbnail

이 글은 '이웅모'님의 '모던 자바스크립트 Deep Dive' 책을 통해 공부한 내용을 정리한 글입니다. 저작권 보호를 위해 책의 내용은 요약되었습니다.

이벤트(event)란 어떠한 사건을 의미하며, 이벤트가 발생하는 시점이나 순서를 사전에 인지할 수 없으므로 일반적인 제어 흐름과는 다른 방식이 필요하다.

브라우저는 이벤트를 감지할 수 있으며 이벤트 발생 시 이를 통지해준다. 보통의 경우는 이벤트가 발생하기 전에는 실행되지 않다가 이벤트가 발생하면 실행되는 함수를 연결하며 이를 이벤트 핸들러라 한다.

이벤트 루프 (Event Loop)

브라우저는 단일 쓰레드(single-thread)에서 이벤트 드리븐(event-driven) 방식으로 동작한다. 웹 애플리케이션은 단일 쓰레드인 것에 비해 동시에 여러 개의 task가 처리되는 것처럼 보이는데, 이를 동시성(Concurrency)라고 하며 이를 지원하는 것이 바로 이벤트 루프이다.

  • Event Queue (Task Queue) : 비동기 처리 함수, 비동기식 이벤트 핸들러 등의 콜백함수가 보관되는 영역으로 이벤트 루프에 의해 Call Stack이 비어졌을 때 순차적으로 Call Stack으로 이동되어 실행
  • Event Loop : Call Stack 내에서 현재 실행중인 task가 있는지, Event Queue에 task가 있는지 반복 확인. 만약 Call Stack이 비어있다면 Event Queue 내의 task를 Call Stack으로 이동시킴

위와 같은 특성 때문에 다음 예제 코드는 다음과 같은 결과를 나타낸다.

function a() {
  console.log('a');
  b();
}

function b() {
   setTimeout(() => {
    console.log('b');
  }, 0);
  c();
}

function c() {
  console.log('c');
}

a(); // a -> c -> b

이벤트 종류

대표적인 이벤트 몇 가지는 다음과 같다. 추가 내용은 여기(MDN)서 확인해보자.

  • load : 웹 페이지 로드 완료 시
  • resize : 브라우저 창 크기 조절 시
  • scroll : 사용자가 페이지를 위아래로 스크롤 시
  • keyup : 누르고 있던 키를 뗄 때
  • click : 마우스 버튼을 클릭 시
  • dbclick : 마우스 버튼을 더블 클릭 시
  • focus/focusin : 요소가 포커스를 얻었을 때
  • blur/focusout : 요소가 포커스를 잃었을 때
  • change : select box, checkbox, radio button의 상태가 변경 시
  • submit : form submit 시
  • reset : reset 버튼 클릭 시
  • copy : 콘텐츠 복사 시

이벤트 핸들러 방식

3가지 방식이 있다.

인라인 이벤트 핸들러 방식

  • this : 전역 객체 window

HTML 요소의 이벤트 핸들러 어트리뷰트에 이벤트 핸들러를 등록하는 방식이다. 더 이상 사용되지 않는다. on으로 시작하는 이벤트 어트리뷰트의 값으로 함수 호출을 전달한다.

<button onclick="click()">Click</button>
<script>
    function click() {
      alert('Clicked!');
    }
</script>

이벤트 어트리뷰트의 값으로 전달한 함수 호출은 런타임 때 즉시 실행되어 평가된 값이 전달되는 것은 아니다. 이벤트 어트리뷰트 키를 이름으로 갖는 함수를 암묵적으로 정의하고 함수의 몸체에 이벤트 어트리뷰트의 값으로 전달한 함수 호출을 문으로 갖는다. 즉, 다음과 같다.

function onclick(event) {
  click();
  // 추가로 할당 가능
}

따라서 여러 개의 문을 전달할 수 있다.

<button onclick="click(); dbclick()">Click</button>
<script>
    function click() {
      alert('Clicked!');
    }
  
    function dbclick() {
  	  alert('Double Clicked!');
    }
</script>

이벤트 핸들러 프로퍼티 방식

  • this : 이벤트에 바인딩된 요소

인라인 이벤트 핸들러 방식처럼 HTML과 Javascript가 뒤섞이는 문제는 해결할 수 있는 방식이다. 하지만 이벤트 핸들러 프로퍼티에 하나의 이벤트 핸들러만을 바인딩할 수 있다는 단점이 있다.

<button>Click</button>
<script>
    const btn = document.querySelector('button');
    btn.onclick = function () {
      alert('Button Clicked!');
    };
</script>

addEventListener 메서드 방식

  • this : 이벤트 리스너에 바인딩된 요소

대상 DOM 요소에 이벤트를 바인딩하고 해당 이벤트가 발생했을 때 실행될 콜백 함수(이벤트 핸들러)를 지정한다.

  • 하나 이상의 이벤트 핸들러 추가 가능
  • 캡처링과 버블링 지원
  • HTML 요소뿐만아니라 모든 DOM 요소(HTML, XML, SVG)에 대해 동작
<button>Click</button>
<script>
    const btn = document.querySelector('button');
    btn.addEventListener('click', () => click());
  
    function click() {
      alert('Clicked!');
    }
</script>

이벤트 흐름

계층적 구조에 포함되어 있는 HTML 요소에 이벤트가 발생할 경우 연쇄적인 반응이 일어난다. 이를 이벤트 전파(Event Propagation)이라 한다. 전파 방향에 따라 캡처링버블링으로 구분된다.

addEventListener 메서드의 세번째 매개변수에 true를 설정하면 캡처링으로 전파되는 이벤트를 캐치하고 false 또는 미설정시 버블링으로 전파되는 이벤트를 캐치한다.

Event.stopPropagation()

이와 같은 이벤트 전파를 차단하기 위해서는 Target 부분에 event.stopPropagation() 메서드를 사용하면 된다.

document.querySelector('button').addEventListener('click', function(event) {
  event.stopPropagation();
});

Event.preventDefault()

stopPropagation 메서드와 비슷하게 요소의 기본 동작을 방지하는 메서드이다. 폼을 submit하거나 링크를 클릭하면 다른 페이지로 이동하게 된다. 이와 같이 요소가 가지고 있는 기본 동작을 중단시키기 위한 메소드가 preventDefault()이다.

document.querySelector('form').addEventListener('submit', function(event) {
  event.preventDefault();
});

이벤트 객체 (Event)

이벤트가 발생하면 event 객체는 동적으로 생성되며 이벤트를 처리할 수 있는 이벤트 핸들러에 인자로 전달된다.

const btn = document.querySelector('button').addEventListener('click', (event) => {
  console.log(event);
});

위와 같이 event 객체에는 많은 정보를 담고 있다. (위 사진은 중략되었음)

event 객체는 이벤트 핸들러에 암묵적으로 전달된다. 그러나 이벤트 핸들러를 선언할 때, event 객체를 전달받을 첫 번째 매개변수를 명시적으로 선언하여야 한다.

대표적인 몇 가지 프로퍼티를 알아보자.

Event.target

이벤트를 발생시킨 요소를 가리킨다.

Event.currentTarget

이벤트에 바인딩된 DOM 요소를 가리킨다.

target vs currentTarget vs this

<div style="width:100px; height:100px; background-color:red">
    <button>click</button>
</div>
const div = document.querySelector('div').addEventListener('click', clickbtn);
const btn = document.querySelector('button').addEventListener('click', clickbtn);

function clickbtn(event) {
  console.log(this);
  console.log(event.target);
  console.log(event.currentTarget);
}

button을 클릭하면 다음과 같은 결과가 나온다.

  • Target 단계

  • Bubbling 단계

콜백함수에서 화살표 함수를 쓰지 않는다면 this === currentTarget이 성립한다.

이벤트 위임

이벤트 위임(Event Delegation)은 다수의 자식 요소에 각각 이벤트 핸들러를 바인딩하는 대신 하나의 부모 요소에 이벤트 핸들러를 바인딩하는 방법이다. 이는 이벤트가 이벤트 흐름에 의해 이벤트를 발생시킨 요소의 부모 요소에도 영향(버블링)을 미치기 때문에 가능한 것이다.

<div style="width:100px; height:100px; background-color:red">
  <button id="b1">click</button>
  <button id="b2">click</button>
  <button id="b3">click</button>
  <button id="b4">click</button>
</div>
const div = document.querySelector('div').addEventListener('click', clickbtn);

function clickbtn(event) {
  console.log(event.target);
}

버튼을 각각 눌러보면 다음과 같은 결과가 나온다.

profile
woowacourse 5th FE

0개의 댓글