이벤트 리스너 내부에서의 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에 넘겨주어야 정상적으로 삭제가 된다.
그렇다면 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로 생성되어진 함수를 저장한다. 이벤트가 실행되지 않는것을 확인할 수 있다.
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();
화살표함수 내부에서의 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
는 동일한 타겟에 동일한 이벤트리스너가 등록이 되면 중복된 것들은 제거하기 때문에 다중으로 이벤트가 호출되지 않는다.
좋은 글입니다 굿굿
부끄럽게도 실무 종사하다가 타 회사 과제 할때 bind arrow function 관계 지적 당하면 망각하게 되더라고요.