이벤트 핸들러
이벤트에 반응하려면 이벤트가 발생했을 때 실행되는 함수인 핸들러(handler)를 할당해야 한다.
핸들러는 사용자의 행동에 어떻게 반응할지를 코드로 표현한 것이다.
addEventListener
HTML에 직접 이벤트 핸들러를 등록하는 방법은 단 1개의 이벤트만을 등록할 수 있다. 또한 코드가 길어져 가독성이 떨어지게 된다. addEventListener를 사용하면 복수의 핸들러를 등록할 수 있다.
element.addEventListener(event, handler, [options]);
- event: 이벤트 이름. “click”, “scroll” 등
- handler: 핸들러 함수
- option
- once: true이면 이벤트가 트리거 될 때 리스너가 자동으로 삭제된다.
- capture: 어느 단계에서 이벤트를 다뤄야 하는지를 알려주는 프로퍼티
- passive: true이면 리스너에서 지정한 함수가 preventDefault()를 호출하지 않는다.
event 객체
이벤트가 발생하면 브라우저는 이벤트 객체(event object)를 만든다. 여기에 이벤트에 관한 상세한 정보를 넣은 다음, 핸들러에 인수 형태로 전달한다.
<input type="button" value="클릭해 주세요." id="elem">
<script>
elem.onclick = function(event) {
console.log(event)
console.log(event.type + " 이벤트가 " + event.target + "에서 발생했습니다.");
console.log("이벤트가 발생한 곳의 좌표는 " + event.clientX + ":" + event.clientY +"입니다.");
};
</script>
<button id="elem">클릭해 주세요.</button>
<script>
class Menu {
handleEvent(event) {
let method = 'on' + event.type[0].toUpperCase() + event.type.slice(1);
this[method](event);
}
onMousedown() {
elem.innerHTML = "마우스 버튼을 눌렀습니다.";
}
onMouseup() {
elem.innerHTML = "그리고 버튼을 뗐습니다.";
}
}
let menu = new Menu();
elem.addEventListener('mousedown', menu);
elem.addEventListener('mouseup', menu);
</script>
위와 같이 메서드를 사용해서 이벤트를 관리해 줄 수도 있다. 이벤트 핸들러만을 따로 모으기 좋은 방법인 것 같다.
this
addEventListener()를 사용해 요소에 수신기를 부착하게 되면 수신기 내부의 this 값은 대상 요소를 가리키게 되며, 이는 수신기가 매개변수로 받게 되는 이벤트 객체의 currentTarget 속성과 같습니다. -MDN-
<button id="btn">클릭해주세요!</button>
<script>
document.querySelector('#btn').addEventListener('click', onClick)
function onClick(e) {
console.log(e.currentTarget === this) //true
};
</script>
- event.target은 실제 이벤트가 시작된 ‘타깃’ 요소이다. 버블링이 진행되어도 변하지 않는다.
- this는 ‘현재’ 요소로, 현재 실행 중인 핸들러가 할당된 요소를 참조한다.
화살표 함수로 작성되었을 경우 화살표 함수의 규칙을 따르게 된다. 따라서 아래 예시에서는 결과로 window가 나오게 된다.
<button id="btn">클릭해주세요!</button>
<script>
document.querySelector('#btn').addEventListener('click', (e)=>{
console.log(this)
})
</script>
이벤트 위임
버튼의 개수가 계속 늘어나는 경우 모든 버튼에게 이벤트 핸들러를 등록하는 것은 꽤나 복잡하고 코드가 길어질 것이다. 이벤트 위임을 이해하면 조상 요소에게만 이벤트를 할당하여 자손 요소들의 이벤트를 쉽게 관리할 수 있다.
이벤트 위임을 이해하기 위해서는 우선 버블링(bubbling)을 이해해야한다.
버블링
한 요소에 이벤트가 발생하면, 이 요소에 할당된 핸들러가 동작하고, 이어서 부모 요소의 핸들러가 동작한다. 가장 최상단의 조상 요소를 만날 때까지 이 과정이 반복되면서 요소 각각에 할당된 핸들러가 동작한다.
<style>
body * {
margin: 10px;
border: 1px solid blue;
}
</style>
<form onclick="alert('form')">FORM
<div onclick="alert('div')">DIV
<p onclick="alert('p')">P</p>
</div>
</form>
- <p>에 할당된 onclick 핸들러가 동작
- 바깥의 <div>에 할당된 핸들러가 동작
- 그 바깥의 <form>에 할당된 핸들러가 동작
- document 객체를 만날 때까지, 각 요소에 할당된 onclick 핸들러가 동작
버블링 중단
이벤트 객체의 메서드인 event.stopPropagation() 를 사용하여 버블링을 중단할 수 있다. 하지만 이는 여러가지 문제를 발생시킬 수 있기 때문에 정말 필요한 경우를 제외하고는 사용하지 않도록 하자. 이벤트 버블링을 막을 경우는 거의 없고 이또한 커스텀 이벤트를 통해 해결 할 수 있다.
이벤트 위임(event delegation)
이벤트 위임을 사용하면 각 요소에 이벤트를 할당하지 않고 조상에게 이벤트를 할당하고 자손 요소들을 한꺼번에 조작할 수 있다.
<div id="menu">
<button data-action="save">저장하기</button>
<button data-action="load">불러오기</button>
<button data-action="search">검색하기</button>
</div>
<script>
class Menu {
constructor(elem) {
this._elem = elem;
elem.onclick = this.onClick.bind(this); // (*)
}
save() {
alert('저장하기');
}
load() {
alert('불러오기');
}
search() {
alert('검색하기');
}
onClick(event) {
let action = event.target.dataset.action;
if (action) {
this[action]();
}
};
}
new Menu(menu);
</script>
각 버튼들에게 이벤트를 등록하지 않고도 원하는 작업을 실행할 수 있다. 이는 훨씬 간결한 코드를 작성할 수 있게 해준다. 또한 버튼이 추가로 생성/삭제되어도 보다 유연하게 이에 대처할 수 있게 된다.
참고
https://developer.mozilla.org/ko/docs/Web/API/EventTarget/addEventListener
'JavaScript' 카테고리의 다른 글
[Javascript] Event Loop (0) | 2022.04.24 |
---|---|
[Javascript] 깊은 복사(deep copy) (0) | 2022.03.27 |
웹팩(webpack) 알러지 치료하기 - 1 (0) | 2022.02.27 |
[javaScript] export와 export default의 차이점 (0) | 2021.11.30 |
[Javascript] Async, Await (0) | 2021.11.01 |