Event handling (overview) - Event reference | MDN
Introduction to events - Learn web development | MDN
HTML attribute reference - HTML: HyperText Markup Language | MDN
thisthis가 무엇을 가리키는지 쉽게 이해할 수 있다.가장 오래된 방법이자 현재는 사용하지 말아야 할 방법이다.
이 방법은 HTML element의 “event handler attribute” (예를들어 onclick등)에 값을 지정하는 방법으로 핸들러 함수를 지정한다. 아래 예시를 보자.
<!DOCTYPE html>
<html lang="en">
<head>
...
</head>
<body>
<button id="myBtn" onclick="bgChange()">Press me</button>
<script>
function bgChange() { // 이름이 bgChange인 이유는 신경쓰지 말자.
console.log("clicked");
console.log(this); // Window{...} (global object)
}
console.log(document.getElementById('myBtn').onclick);
/*
ƒ onclick(event) {
bgChange()
}
*/
</script>
</body>
</html>
내부적으로 어떻게 동작하는지 알아보자.
event handler attribute는 문자열string을 값으로 받는데(onclick=”bgChange()”부분), JavaScript는 이 문자열 값을 사용하여 function name(/**args**/) {body}와 같은 형태의 JS 함수를 합성한다. 여기서 name은 attribute의 name(위 예시에서 onclick), body는 attribute의 value(위 예시에서 bgChange())이다.
즉 위 예시 코드는 결과적으로 function onclick() { bgChange(); console.log(this);} 라는 함수를 생성한다.
예를들면 아래와 같은 코드도 가능하다. 자동으로 onclick이라는 이름의 함수가 생성되기 때문에 생성된 함수 스스로를 참조할 수 있는 모습이다.
<div onclick="console.log(onclick)">Click me!</div>
그리고 JS는 생성한 함수를 해당 HTMLElement 객체의 이벤트 프로퍼티에 할당한다. (즉 위 예시에서는 button element 객체의 onclick 프로퍼티에 생성된 함수가 할당되는 것이다).
document.getElementById('myBtn').onclick를 콘솔로그로 출력해보면, 주석에 써있는 것처럼 ƒ onclick(event) { bgChange(); console.log(this); } 가 나오는 것을 확인할 수 있다.이런 원리로 결과적으로 해당 이벤트 발생 시에 우리가 정의한 핸들러 함수가 실행되는 것이다.
여기서 우리는 두가지 사실을 추가로 알 수 있다.
this 는 전역 객체를 가리킨다.
onclick이라는 생성된 함수 내부에서 우리가 정의한 핸들러 함수인 bgChange를 따로 다시 호출하는 방식으로 작동하기 때문이다. 즉 일반 함수로서 호출이기에,event 객체를 인자로 받는다.
그래서 핸들러 함수 내부에서 reserved된 event 객체를 사용할 수 있다. 아래처럼 말이다.
<div onclick="console.log(event)">Click me!</div>
그럼 만약 아래처럼 하면 어떻게 될까??
<!DOCTYPE html>
<html lang="en">
<head>
...
</head>
<body>
<button id="myBtn" **onclick="console.log(this)"**>Press me</button>
</body>
</html>
this는 HTMLElement 객체인 버튼을 가리키게 된다.. 어째서일까?
f onclick() { console.log(this) } 이다. 이 함수가 HTMLElement.onclick 프로퍼티에 할당되어 이벤트가 발생할 시, 해당 함수를 호출하게 되는데, 이러면 메소드로서 호출된 상황이기 때문에 메소드 내부의 this는 호출 주체인 HTMLElement 객체를 가리키게 되는 것이다.어쨌거나 이 방법은 outdated 되었기 때문에 사용하지 말아야 한다.
HTML과 JavaScript를 같이 사용하게 되면 가독성이 떨어지고 유지보수하기에 좋지 않기 때문에 분리해 주는 것이 good practice이다.
만약 버튼이 1개가 아니라 100개가 있다면? 일일이 100개의 attribute를 추가해 주어야 할 것이다. 거기에 더해 만약 핸들러를 수정해야 한다면? 악몽이 될 것이다.
inline 방식 대신 아래와 같이 간단한 방법을 사용할 수 있다.
const buttons = document.querySelectorAll("button");
for (const button of buttons) {
button.addEventListener("click", bgChange);
}
게다가 많은 server configurations가 보안 목적으로 inline JavaScript를 금지한다고 한다. (이건 구체적으로 어떤 예시가 있는지 궁금하네)
다만 원리를 공부하는 과정에서 DOM element 객체와, this에 대해 더 깊게 이해할 수 있으니 중요하다.
이벤트의 타겟 HTML element object의 onevent 프로퍼티에 핸들러 함수를 할당하는 방법이다.
위에서 복잡한 내용은 이미 다 다뤘기 때문에 간단하게 짚고 넘어가자
const btn = document.querySelector("button");
function greet(event) {
console.log("greet:", event);
}
btn.onclick = greet;
이 방식의 특징은 element의 각 이벤트에 단 하나의 핸들러만 할당할 수 있다는 것이다.