자바스크립트를 공부 하던 중
function updateHandler() {
console.(this.value); // input요소가 변화할 때 마다 값이 출력 됨
}
const input = document.querySelectorAll('input');
input.addEventListener('change',updateHandler);
이와 비슷한 코드를 작성할 일이 있었다.
처음에 updateHandler를 화살표 함수로 작성했다가, this가 전역객체를 가리키는 바람에 에러가 나서 수정을 했다.
그런데 생각해보니 일반함수를 함수로 실행하면 this가 전역객체를 가리켜야 맞는 것 같은데 이벤트가 바인딩된 요소를 가리키고 있는게 의문들었다.
그래서 먼저 이벤트에 관해서 정리해보고, 이벤트 핸들러 내부의 this에 관련된 내용을 정리해보려고 한다.
이벤트가 발생했을 때, 브라우저에게 이벤트 핸들러의 호출을 위임하는 방법은 3가지가 있다.
HTML 태그에 인라인 방식으로 이벤트 핸들러를 등록한다.
<button onclick="handleClick()"> // 함수 호출문의 형태로 넣어준다.
이벤트 핸들러의 어트리뷰트 값은 암묵적으로 생성될 이벤트 핸들러의 함수 몸체를 의미한다.
따라서 이 방식으로 이벤트 핸들러를 등록하면 handleClick()
함수 자체가 이벤트 핸들러가 되는 것이 아니고, 암묵적으로 생성된 onClick()
함수가 핸들러로 등록이 되고, onClick()
함수 내부에 handleClick()
함수가 실행문으로 포함되는 것이다.
객체의 프로퍼티에 값을 할당하듯 등록하는 방식
<script>
function printThis() {
console.log(this);
}
//이벤트 핸들러 프로퍼티 방식
document.getElementById('btn').onclick = printThis;
</script>
이벤트 핸들러 프로퍼티는 하나의 이벤트 핸들러만 등록할 수 있다.
객체의 프로퍼티는 프로퍼티 값을 갱신할 수 있기 때문에 같은 이벤트 핸들러 프로퍼티에 여러 번 이벤트 핸들러를 등록해도 결국 재할당으로 동작되어 제일 마지막에 바인딩된 이벤트 핸들러만 등록된다.
EventTarget.prototype.addEventListener
메서드를 사용하여 이벤트 핸들러를 등록하는 방식
이벤트 타깃.addEventListener('이벤트 타입',이벤트 핸들러[,캡쳐 사용 여부]);
첫 번째 매개변수인 이벤트 타입 문자열에는 이전의 등록방식과는 달리 on을 붙이지 않는다.
addEventListener 메서드는 하나 이상의 이벤트 핸들러를 등록할 수 있다. 이때 이벤트 핸들러는 등록된 순서대로 호출된다.
단, 참조가 동일한 이벤트 핸들러를 중복 등록하면 하나의 핸들러만 등록된다.
이벤트가 발생하면 이벤트 객체가 생성되어 이벤트 핸들러의 첫 번째 인수로 전달된다.
이 때 이벤트 객체는 해당 이벤트에 관한 다양한 정보가 담겨있다.
이벤트 객체를 전달받으려면 이벤트 핸들러를 정의할 때 객체를 전달받을 매개변수를 명시적으로 선언해야하며 이름은 자유롭게 쓸 수 있다.(보통 e라고 쓴다)
그러나 이벤트 핸들러 어트리뷰트 방식으로 등록했다면 반드시 호출문의 인자로 'event'를 넣어줘야 한다.(암묵적으로 생성된 이벤트 핸들러의 첫 매개변수의 이름이 event로 정해져있기 때문이다)
모든 이벤트 객체가 상속받는 공통적인 프로퍼티가 있다.
<!DOCTYPE html>
<html>
<body>
<input type="button" value="click" id="btn" onclick="printThis()" />
<script>
function printThis() {
console.log(this);
}
</script>
</body>
</html>
window 객체가 출력된다.
<!DOCTYPE html>
<html>
<body>
<input type="button" value="click" id="btn" />
<script>
function printThis() {
console.log(this);
}
document.getElementById('input').onclick = printThis;
</script>
</body>
</html>
input 요소가 출력된다.
<!DOCTYPE html>
<html>
<body>
<input type="button" value="click" id="btn" />
<script>
function printThis() {
console.log(this);
}
document.getElementById('btn').addEventListener('click', printThis);
</script>
</body>
</html>
input 요소가 출력된다.
이벤트 핸들러 프로퍼티 방식과 addEventListener 메서드 방식 모두 이벤트 핸들러 내부의 this는 이벤트를 바인딩한 DOM 요소를 가리킨다.
유사한 여러 개의 요소가 있고, 모두 같은 이벤트 핸들러가 등록되었다면 그 이벤트 핸들러는 이벤트가 일어난 요소를 참조하는 것이 편할 것이다.
따라서 이벤트 핸들러 내부의 this는 이벤트 객체의 currentTarget 프로퍼티와 같다.
위에서 정리한대로 이벤트 핸들러 어트리뷰트 값은 사실 암묵적으로 생성될 이벤트 핸들러의 함수 몸체를 의미한다.
<input type="button" value="click" id="btn" onclick="printThis()" />
이렇게 이벤트 핸들러 어트리뷰트 방식으로 이벤트 핸들러를 등록했다면
<script>
// 이벤트 핸들러 어트리뷰트이름과 같은 이름의 onclick 함수 생성
function onclick(event) {
printThis();
}
// 동일한 이름인 onclick프로퍼티에 암묵적으로 생성한 onclick함수를 할당한다.
document.querySelector('#btn').onclick = onclick;
</script>
내부적으로는 위와 같이 동작하여 printThis()
함수는 암묵적으로 생성된 onClick()
함수 내에서 일반함수로서 호출되기 때문에, this가 전역객체를 가리키게 되는 것이다.
그리고 이벤트 핸들러 내부의 this는 이벤트를 바인딩한 DOM요소를 가리키기 때문에, 암묵적으로 생성된 onClick함수 내부의 this또한 이벤트를 바인딩한 DOM요소를 가리킨다.
이는 아래의 코드로 확인할 수 있다.
<!DOCTYPE html>
<html>
<body>
<input type="button" value="click" id="btn" onclick="console.log(this)" />
</body>
</html>
이 경우 내부적으로는 아래와 같이 동작하기 때문에 this는 input요소를 가리키게 된다.
<script>
// 이벤트 핸들러 어트리뷰트이름과 같은 이름의 onclick 함수 생성
// 생성된 onClick은 이벤트 핸들러로 등록되기 떄문에 this는 이벤트를 바인딩한 DOM요소를 가리킨다.
function onclick(event) {
console.log(this);
}
// 동일한 이름인 onclick프로퍼티에 암묵적으로 생성한 onclick함수를 할당한다.
document.querySelector('#btn').onclick = onclick;
</script>
Reference
https://velog.io/@ursr0706/이벤트-핸들러-내부의-this-2gyujqm2
https://velog.io/@ursr0706/이벤트