이벤트 리스너와 bind

맨날·2021년 5월 20일
2

이벤트 리스너와 bind

이벤트 리스너 내부에서의 this는 이벤트가 발생한 요소를 가리킨다. 이때 이벤트가 발생한 요소가 아닌 다른 값을 this로 사용하고 싶은 경우가 존재한다.

아래와 같이 코드를 작성 후 타겟 요소에 클릭을 하면 에러가 발생한다.

class EventObject {
	element = null;

	constructor() {
		this.element = document.querySelector('.myName'); 	
	}

	getName() {
		return 'My name is EventObject'
	}

	addEvent() {
		this.element.addEventListener('click', this.handlerClick); 
	}

	handlerClick() {
		alert(this.getName()); // this는 타겟 요소 이기 때문에 에러발생
	}
}

아래와 같이 bind 메소드를 이용하면 정상적으로 작동한다.

class EventObject {
	(...)
	addEvent() {
		this.element.addEventListener('click', this.handlerClick.bind(this)); 
	}

	handlerClick() {
		alert(this.getName()); // this가 바인딩 되어서 정상적으로 작동한다.
	}
}

이벤트 리스너를 추가 후 삭제를 해야하는 경우도 존재한다. 아래는 삭제하는 코드를 추가해 보았다.

class EventObject {
	(...)
	removeEvent() {
		/*
		 * 아래의 코드를 추가하여도 클릭 이벤트가 작동하게 된다.
		 */
		this.element.removeEventListener('click', this.handlerClick);
		this.element.removeEventListener('click', this.handlerClick.bind(this));
	}
}

위와 같이 removeEventListener 메소드를 추가하여도 클릭 이벤트가 작동하는 것을 확인할 수 있다.

addEventListener 와 removeEventListener

두 함수의 처음 두개의 파라미터는 아래와 같다.

  • 타입
  • 이벤트리스너

여기서 주목해야 할 점은 이벤트리스너이다. 이벤트를 제거하기 위해서는 addEventListener에서 넘겨주었던 동일한 이벤트리스너를 removeEventListener에 넘겨주어야 정상적으로 삭제가 된다.

그렇다면 EventObject의 removeEvent가 정상적으로 작동하지 않는 이유에 대해서 알아보자.

이벤트 등록시에 이벤트리스너에 bind 메소드를 호출하였다. bind 메소드는 첫번째 인자로 넘겨받은 값을 this로 설정 후 새로운 함수를 반환한다.

아래의 코드를 실행해보면 두 함수가 같지 않음을 알 수 있다.

const object = {
	name: 'object',
	getName() {
		return this.name;
	}
}

const getName = object.getName;
const bindGetName = object.getName.bind(object);

console.log(getName === bindGetName); // false

다시 돌아와서 아래의 코드를 보면 3개의 이벤트리스너 함수가 모두 다른 값이라는 것을 알 수 있을 것이다. 그래서 이벤트리스너 제거가 되지 않는다.

class EventObject {
	(...)
	addEvent() {
		this.element.addEventListener('click', this.handlerClick.bind(this)); 
	}
	
	removeEvent() {
		this.element.removeEventListener('click', this.handlerClick);
		this.element.removeEventListener('click', this.handlerClick.bind(this));
	}
}

bind를 사용할때 이벤트 제거하기

bind를 사용해야 하는 경우 이벤트를 정상적으로 제거하기 위해서 몇가지를 생각해보았다. 현재 나의 실력으로는 이 정도까지인것 같다.

1. 이벤트리스너를 저장

이벤트리스너 변수를 생성하여 bind로 생성되어진 함수를 저장한다. 이벤트가 실행되지 않는것을 확인할 수 있다.

class EventObject {
	(...)
	eventListener = null;

	constructor() {
		this.eventListener = this.handlerClick.bind(this);
	}

	addEvent() {
		this.element.addEventListener('click', this.eventListener); 
	}

	removeEvent() {
		this.element.removeEventListener('click', this.eventListener);
	}
}

const a = new EventObject();
a.addEvent();
a.removeEvent();

2. 이벤트리스너를 하나의 메소드로 더 감싼다.

화살표함수 내부에서의 this는 상위스코프의 this를 참조한다라는 걸 이용한다. 새롭게 감싸는 메소드를 화살표함수로 작성하면 내부에서의 this는 EventObject를 가리키게 된다. 그래서 bind를 사용하지 않아도 된다.

class EventObject {
	(...)
	wrapEvent = () => {
		this.handlerClick();
	}

	addEvent() {
		this.element.addEventListener('click', this.wrapEvent); 
	}

	removeEvent() {
		this.element.removeEventListener('click', this.wrapEvent);
	}
}

동일 이벤트 리스너는 등록해도 추가되지 않는다

기존의 이벤트를 지우고 동일한 이벤트리스너를 다시 등록하는 코드를 본적이 있다.

아래의 코드처럼 기존의 이벤트를 지우고 다시 이벤트를 등록하고 있다.

const eventListener = () => 'eventListener';
const element = document.querySelector('.element');

element.removeEventListener('click', eventListener);
element.addEventListener('click', eventListener);

새로운 이벤트리스너를 등록 하는 경우가 아니라면 이벤트를 삭제할 필요가 없다. 위와 같은 코드를 작성하는 이유는 이벤트 등록시 기존에 등록된 이벤트가 존재해서 다중으로 이벤트가 호출되는 것을 방지하기 위해서일것이다.

하지만 addEventListener는 동일한 타겟에 동일한 이벤트리스너가 등록이 되면 중복된 것들은 제거하기 때문에 다중으로 이벤트가 호출되지 않는다.

1개의 댓글

comment-user-thumbnail
2022년 4월 30일

좋은 글입니다 굿굿
부끄럽게도 실무 종사하다가 타 회사 과제 할때 bind arrow function 관계 지적 당하면 망각하게 되더라고요.

답글 달기