▼ HTML, CSS, JavaScript 문서
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="./style.css">
<title>Document</title>
</head>
<body>
<div id="content">
<h1 id="title">오늘 할 일</h1>
<ul id="list">
<li class="item">자바스크립트 공부</li>
<li class="item">구름이 산책</li>
<li class="item">게임</li>
<li class="item">독서</li>
<li class="item">청소</li>
</ul>
</div>
<script src="index.js"></script>
</body>
</html>
.item {
margin: 10px 0;
cursor: pointer;
}
.done {
opacity: 0.5;
text-decoration: line-through;
}
// 이벤트 위임 (Event Delegation)
const list = document.querySelector("#list");
for (let item of list.children) {
item.addEventListener("click", function (e) {
e.target.classList.toggle("done");
});
}
할 일 목록에 아이템을 클릭하면 취소선이 그어지면서 완료된 모습이 되고
다시 클릭하면 원래 모습으로 돌아오는 코드를 작성했다.
'querySelector'로 list 라는 아이디를 가진 요소에 접근한 다음
children 프로퍼티를 활용해서 for of 문으로 아이템 각각의 이벤트 핸들러를 모두 등록했다.
이벤트 핸들러를 조금 더 보자면,
이벤트 객체에 'target' 프로퍼티로 이벤트가 발생한 요소에 접근한 다음
classList의 toggle 메소드를 활용해서 'done' 클래스를 적용했다.
이벤트 위임 (Event Delegation)
그런데 만약 아래와 같이 새로운 아이템을 추가하면
추가된 아이템에는 이벤트 핸들러가 동작하지 않는다.
// 이벤트 위임 (Event Delegation)
const list = document.querySelector("#list");
for (let item of list.children) {
item.addEventListener("click", function (e) {
e.target.classList.toggle("done");
});
}
//새로운 아이템 추가
const li = document.createElement("li");
li.classList.add("item");
li.textContent = "빨래하기";
list.append(li);
그래서 매번 추가할 떄마다 이벤트 핸들러를 새로 등록해야 하는 번거로움이 생기게되는데,
이벤트 버블링 개념에 따르면 부모 요소가 자식 요소에서 발생한 이벤트를 감지할 수 있고
이벤트 객체에 target 프로퍼티가 항상 이벤트 발생 위치를 담고 있기 때문에
위처럼 아이템 각각의 이벤트 핸들러를 등록하는 것이 아니라 (item.addEventListener ···.)
아래처럼 부모 요소인 리스트에 이벤트 핸들러를 등록해주면
모든 자식 요소의 이벤트를 다룰 수 있게 된다.
// 이벤트 위임 (Event Delegation)
const list = document.querySelector("#list");
// for (let item of list.children) {
// item.addEventListener("click", function (e) {
// e.target.classList.toggle("done");
// });
// }
list.addEventListener("click", function (e) {
e.target.classList;.toggle('done')
});
//새로운 아이템 추가
const li = document.createElement("li");
li.classList.add("item");
li.textContent = "빨래하기";
list.append(li);
리스트에 이벤트 핸들러를 하나밖에 등록하지 않았지만
각각 아이템에 이벤트 핸들러를 등록한 것처럼 동작하고
나중에 추가한 아이템도 이벤트 핸들러가 잘 동작하는 것을 확인할 수 있다.
이렇게 자식 요소에서 발생하는 이벤트를 부모 요소에서
다루는 방식을 가리켜서 이벤트 위임이라고 한다.
그런데 문제는 부모 요소에 등록된 이벤트 핸들러이기 때문에
자식 요소를 제외한 온전히 부모인 요소를 클릭해도 아래와 같이 이벤트 핸들러가 동작한다.
그러니까, 아이템 영역이아니라
아이템 영역에서 조금 벗어난,
리스트에만 존재하는 영역을 클릭하게 되면 리스트에도 'done' 클래스가 추가된 것을 볼 수있는데,
이렇게 리스트에도 'done' 클래스가 추가된 것을 확인 할 수 있다.
그래서 이벤트 위임을 할 때는 명확하게 원하는 요소에서
의도한 동작이 일어나게끔 따로 처리를 해줘야 한다.
지금과 같은 상황에서는 아래와 같이 target 프로퍼티의 tagName이 li인지 확인하거나
classList의 contains 메소드를 활용해서 item이라는 클래스를 가지고 있는지 확인하면 해결할 수 있다.
// 이벤트 위임 (Event Delegation)
const list = document.querySelector("#list");
// for (let item of list.children) {
// item.addEventListener("click", function (e) {
// e.target.classList.toggle("done");
// });
// }
// list.addEventListener("click", function (e) {
// e.target.classList.toggle("done");
// });
list.addEventListener("click", function (e) {
//if (e.target.tagName === 'Li')
if (e.target.classList.contains("item")) {
e.target.classList.toggle("done");
}
});
//새로운 아이템 추가
const li = document.createElement("li");
li.classList.add("item");
li.textContent = "빨래하기";
list.append(li);
이렇게 아이템 부분만 클릭 했을 때 원하는 이벤트 핸들러가 동작하고
리스트 부분은 이제 반응 하지 않는걸 확인할 수 있다.
코드를 풀어보자면..
tagName 프로퍼티를 사용하여 클릭한 요소의 태그를 가져오거나,
(단 TagName의 동등기호 연산자를 사용할면 태그이름을 대문자로 작성해야한다.)
classList의 contains 메소드는 파라미터로 전달하는 값이
해당 요소의 클래스 속성에 있는지를 확인해서 불린형태로 결과를 리턴해주는 메소드이다.
이 방법들 뿐만 아니라 이벤트 객체 target 프로퍼티를 잘 활용하면 다른 방법으로도 결과 값을 가져올 수 있으며
중요한건 이벤트 위임을 할 때 원하는 자식 요소에서 필요한 동작이 실행되게끔 처리 해줘야된다.
이렇게 버블링을 활용한 이벤트 위임을 하여
새로운 자식 요소를 추가하거나 혹은 삭제하더라도
이벤트에 대한 제어를 자식 요소에 신경쓰지 않아도 되기 때문에
유연하게 코드를 작성할 수 있다.
이뿐만 아니라 여러 개 이벤트 핸들러를 만들지 안아도 된다는 점은
코드를 적게 작성해도 되는 효율 뿐만 아니라
실제로 코드가 동작할 때 프로그램의 성능에도 긍정적인 영향일 미치기 때문에
이벤트를 다루고자 할 때 이벤트 위임을 우선적으로 고려한 다음에
불가피한 경우에 개별적으로 이벤트 핸들러를 등록하는 방식을 사용하면 된다.
참고로 이벤트 위임은 버블링을 활용한 방식이기 때문에
자식요소 중에서 버블링을 막는 이벤트가 있을 경우 동작하지 않기 때문에
의도한대로 이벤트가 동작하지 않는다.
특별한 경우가 아니라면 이벤트 버블링을 막는 건 되도록이면 하지 않는게 좋다.
▼GitHub
'JS' 카테고리의 다른 글
(18) Interactive JavaScript (Event) - 마우스 버튼 이벤트 (0) | 2024.11.12 |
---|---|
(17) Interactive JavaScript (Event) - 브라우저의 기본 동작 제어 (0) | 2024.11.11 |
(15) Interactive JavaScript (Event) - 이벤트 버블링 (0) | 2024.11.07 |
(14) Interactive JavaScript (Event) - 이벤트 객체 (0) | 2024.11.06 |
(13) Interactive JavaScript (Event) - 이벤트 핸들러 등록하기 (0) | 2024.11.06 |