티스토리 뷰
// 이벤트 위임 (Event Delegation)
const list = document.querySelector('#list');
for(let item of list.children) {
item.addEventListener('click', function(e) {
e.target.classList.toggle('done');
});
}
ul 이벤트를 하나 만들었습니다. 할 일 목록에 아이템을 클릭하면 스타일이 변하면서 할 일을 완료한 듯한 모습이 되고 다시 클릭하면 원래 모습으로 돌아오는데 JavaScript 코드를 한번 살펴보면 querySelector로 list라는 아이디를 가진 요소에 접근한 다음에 children 프로퍼티를 활용해서 for...of 문으로 아이템 각각의 이벤트 핸들러를 모두 등록해 주었습니다.
이벤트 핸들러를 좀 더 살펴보면 이벤트 객체에 target 프로퍼티로 이벤트가 발생한 요소에 접근한 다음에 classList의 toggle 메소드를 활용해서 done이라는 클래스를 토글 해주도록 했는데요.
그런데 이 코드에 한 가지 문제가 있습니다. 이런 식으로 처음에는 없다가 새로운 아이템을 추가하는 상황이 발생하게 되면 이 추가된 아이템에는 이벤트 핸들러가 동작하지 않는다는 겁니다. 그래서 매번 추가할 때마다 이벤트 핸들러를 새로 등록해야 되는 문제가 있습니다. 이럴 때 이벤트 버블링을 활용하면 간단하게 해결할 수가 있습니다.
const li = document.createElment('li');
li.classList.add('item');
li.textContent = '일기 쓰기';
list.append(li);
버블링 개념에 따르면 이 부모 요소가 자식 요소에서 발생한 이벤트를 감지할 수 있고 이벤트 객체에 target 프로퍼티가 항상 이벤트 발생 위치를 담고 있기 때문에 이렇게 아이템 각각의 이벤트 핸들러를 등록하는 것이 아니라 이렇게 부모 요소인 리스트에 이벤트 핸들러를 하나만 등록해 줘도 모든 자식 요소의 이벤트를 다룰 수가 있게 되는 겁니다.
list.addEventListener('click', function() {
e.target.classLsit.toggle('done');
})
코드를 저장하고 실행해보면 리스트에 이렇게 이벤트 핸들러를 하나밖에 등록하지 않았지만 마치 아이템 각각의 이벤트 핸들러 등록한 것처럼 동작하고 나중에 추가한 아이템도 이렇게 이벤트 핸들러가 잘 동작하는 걸 확인할 수가 있습니다. 이렇게 자식 요소에서 발생하는 이벤트를 부모 요소에서 다루는 방식을 가리켜서 이벤트 위임. 영어로는 Event Delegation이라고 부르는데요. 자식 요소의 이벤트를 부모 요소에 위임했다고 이해하면 됩니다.
하지만 여기서도 아직 한 가지 문제가 남아 있습니다. 부모 요소에 등록된 이벤트 핸들러이기 때문에 자식 요소를 제외한 온전히 부모인 요소를 클릭해도 이벤트 핸들러가 동작한다는 겁니다.
아이템 영역이 아니라 아이템 영역에서 조금 벗어나서 리스트에만 존재하는 영역을 클릭하게 되면 이렇게 리스트에도 done이라는 클래스가 추가됩니다. 그래서 이벤트 위임을 할 때는 명확하게 원하는 요소에서 의도한 동작이 일어나게끔 따로 처리를 해줘야 됩니다.
list.addEventListener('click', function(e) {
// if (e.target.tagName === 'Li')
if (e.target.classList.contains('item')) {
e.target.classList.toggle('done');
}
})
지금과 같은 상황에서는 이런 식으로 target 프로퍼티의 tagName이 Li인지를 확인하거나 classList의 contains 메소드를 활용해서 item이라는 클래스를 가지고 있는지 확인하면 해결할 수 있습니다. tagName 프로퍼티는 말 그대로 해당 요소의 태그 이름값을 대문자로 담고 있는 프로퍼티고 classList의 contains 메소드는 파라미터로 전달하는 값이 해당 요소의 클래스 속성에 있는지를 확인해서 불린 형태로 결과를 리턴해주는 메소드입니다.
이벤트 위임을 할 때 원하는 자식 요소에서 필요한 동작이 실행되게끔 처리를 해줘야 합니다.
코드를 저장하고 실행해보면 이제는 딱 아이템 부분만 클릭했을 때 우리가 원하는 이벤트 핸들러가 동작하고 리스트 부분은 반응하지 않는 걸 볼 수 있습니다.
이벤트 위임을 활용하면 새로운 자식 요소를 추가하거나 혹은 삭제하더라도 이벤트에 대한 제어를 이 자식 요소에 신경 쓰지 않아도 되기 때문에 훨씬 더 유연하게 코드를 작성할 수 있다는 장점이 있습니다. 이뿐만 아니라 여러 개 이벤트 핸들러를 만들지 않아도 된다는 점은 코드를 적게 작성해도 되는 효율뿐만 아니라 실제로 이 코드가 동작할 때 프로그램의 성능에도 긍정적인 영향을 미치기 때문에 이벤트를 다루고자 할 때 오늘 배운 이벤트 위임을 우선적으로 고려한 다음에 불가피한 경우에 개별적으로 이벤트 핸들러를 등록하는 방식을 사용하시면 더 좋을 것 같습니다.
참고로 이벤트 위임은 버블링을 활용한 방식이기 때문에 당연히 이런 식으로 자식 요소 중에서 버블링을 막는 이벤트가 있을 경우에는 버블링이 막혀버리기 때문에 의도한 대로 이벤트가 동작하지 않습니다.
const toDoList = document.querySelector('#to-do-list');
// 1. updateToDo 함수를 완성해 주세요
function updateToDo(event) {
if (event.target.classList.contains('item')) {
event.target.classList.toggle('done');
}
}
// 2. 각 li 태그가 아니라 하나의 태그에만 이벤트 핸들러를 등록해 주세요
toDoList.addEventListener('click',updateToDo);
// 테스트 코드
const newToDo = document.createElement('li');
newToDo.textContent = '가계부 정리하기';
newToDo.classList.add('item');
toDoList.append(newToDo);
toDoList.children[2].addEventListener('click', function(e) {e.stopPropagation()});
'프론트엔드 > JavaScript' 카테고리의 다른 글
[다양한 이벤트 알아보기] 01. 마우스 버튼 이벤트 (0) | 2022.11.26 |
---|---|
[이벤트 살펴보기] 12. 브라우저의 기본 동작 (0) | 2022.11.26 |
[이벤트 살펴보기] 08. 캡쳐링 (0) | 2022.11.26 |
[이벤트 살펴보기] 07. 이벤트 버블링 (0) | 2022.11.26 |
[이벤트 살펴보기] 05. 이벤트 객체 프로퍼티 (0) | 2022.11.21 |
- Total
- Today
- Yesterday
- 기본형 데이터
- redux-thunk
- redux thunk
- null
- 비교 연산자
- find
- some
- undefined
- map
- 얕은복사
- redux-middleware
- redux
- filter
- 동적(dynamic) 언어
- redux middleware
- 타입변환
- 불변 객체
- foreach
- EVERY
- 참조형 데이터
- 느슨한 타입(loosely typed)
- findindex
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |