본문 바로가기
FE/browser

[Browser] window.addEventListener

by livemehere 2023. 9. 20.
이벤트 리스너는 웹 개발을 할때 너무나 익숙한 메서드입니다. 하지만 3번째 인자는 많이 생소한데요. 가끔 의도적으로 사용할 때가 생기면서 한번 정리해보았습니다.

목차

1. addEventListener

2.this

3. 세번째 인자

  3-1. capture 속성

  3-2. once 속성

  3-3. passive 속성

  3-4. signal 속성과 abort

  3-5. useCapture 속성

4. removeEventListener 와 메모리 문제


1.addEventListener

EventTarget 인터페이스를 구현한 대상은 addEventListener() 메서드를 가지고, 대상이 이벤트를 수신할 때마다 호출할 콜백을 등록할 수 있습니다.

2.this

addEventListener() 내부 콜백의 this 대상은 e.currentTarget 과 같습니다. (*화살표 함수 x)

my_element.addEventListener("click", function (e) {
  console.log(this); // my_element
  console.log(e.currentTarget === this); // `true`
});

3. 세번째 인자

addEventListener(type, listener);
addEventListener(type, listener, options);
addEventListener(type, listener, useCapture);

3-1. capture 속성

이벤트가 캡처링 단계에서 실행되는지 여부 ( default : false )

이벤트 리스너의 콜백함수는 버블링 단계에서 발생하기 때문에, 자식요소와 부모요소에 동일한 이벤트가 발생한다면, 자식요소가 먼저 실행됩니다. 이를 의도적으로 조절하기 위한 옵션으로 사용할 수 있습니다.

parent.addEventListener('click', (e) => {
    console.log('parent')
},{capture:true})

 

3-2. once 속성

이벤트 리스너가 단 1회 실행되는지 여부 ( default : false )

removeEventListener 없이 의도적으로 한번만 실행할 이벤트를 등록할 때 사용합니다.

parent.addEventListener('click', (e) => {
    console.log('parent')
},{once:true})

3-3. passive 속성

passive 속성이 활성화 되면 내부 콜백에서 절대로 preventDefault() 를 호출하지 않을 것임을 나타냅니다.

만약 속성이 활성화 되었는데, preventDefault() 를 호출하면 콘솔에 경고를 표시합니다.(표시만 함)

기본값은 false 이지만 스크롤, 터치 등 일부 이벤트들은 preventDefault() 를 호출하면, 기본 동작이 수행중인 브라우저의 메인 스레드를 블록할 가능성이 생기고, 그러므로 스크롤 성능이 크게 저하될 수 있기 때문에 일부 브라우저(chrome, Firefox) 에서는 window, Document, body의 touchstart, touchmove 이벤트에 대해서는 기본값이 true 입니다.

parent.addEventListener('mousewheel', (e) => {
    e.preventDefault()
    console.log(e);
}, {passive:true})

3-4. signal 속성

AbortSignal 입니다. AbortSignal 의 signal 레퍼런스를 등록하면, 해당 객체의 abort() 가 호출될 때 이벤트 리스너가 제거됩니다.

보통은 fetch() 요청을 도중에 중단하기 위한 용도로 사용하곤 합니다.

const controller = new AbortController();
const signal = controller.signal;

const parent = document.querySelector('.parent');
parent.addEventListener('click', (e) => {
    console.log('parent')
},{signal})

setTimeout(()=>{
    controller.abort()
},3000)

3-5. useCapture 속성

세번째 인자로 객체가 아닌 boolean 값을 넣으면 useCapture 속성으로 사용됩니다.

단, useCapture 속성이 활성화 된 경우에는 캡처링, 버블링 단계도 아닌 별도의 단계에 발동됩니다.

캡처모드의 이벤트 콜백은 어떤 이벤트 콜백보다 앞서서 발동합니다.

 

아래처럼 하면, 캡처링 단계보다 먼저 실행됩니다.

parent.addEventListener('click', (e) => {
    console.log('pre')
},true)

parent.addEventListener('click', (e) => {
    console.log('parent')
},{capture:true})

// pre
// parent

 

4. removeEventListener 와 메모리 문제

특정 이벤트에대한 이벤트 리스 콜백은 시스템이 제한하는 한도까지는 등록이가능합니다.

여기서 등록되는 콜백은 참조값이 다르면 별개의 메모리로 등록됩니다. 그래서 항상 정적인 참조를 유지하고 removeEventListener 를 해주며 메모리를 정리해주지 않으면 메모리 누수가 쌓이게 됩니다.

 

아래 2가지 누수 상황과 1가지 정적 참조를 유지하는 방법 예시

// 누수1.익명함수가 누적되는 경우
for (let i = 0; i < els.length; i++) {
    els[i].addEventListener("click",function (e) {});
}

// 누수2.참조되는 함수를 등록하지만, 매번 새로 등록되는 함수인경우
for (let i = 0, j = 0; i < els.length; i++) {
  function processEvent(e) {

  }
  els[j].addEventListener("click", processEvent, false);
}


// 정적 참조를 유지하는 방법
function processEvent(e) {
  
}

for (let i = 0; i < els.length; i++) {
  els[i].addEventListener("click", processEvent, false);
}

 

반응형