JS 이벤트 처리 - 2(addEvnetListener)

Gun·2023년 10월 5일

JS

목록 보기
8/12
post-thumbnail
💡 학습목표
   1. addEventListener() 메서드의 활용 
   2. 이벤트 위임(Event Delegation)와 버블링(Bubbling) 

1. addEventListener() 메서드의 활용

시나리오 코드 - 1

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<title>addEventListener</title>
		<link rel="stylesheet" href="reset.css" />
		<style>
			body {
				margin: 16px;
				display: flex;
				justify-content: center;
			}

			ul {
				list-style-type: none;
				padding: 0;
			}
			li {
				background-color: #f9f9f9;
				margin: 8px 0;
				padding: 8px;
				border: 1px solid #ccc;
				display: flex;
				justify-content: space-between;
				align-items: center;
			}
			button {
				margin-left: 8px;
			}
		</style>
	</head>
	<body>
		<div>
			<h2>EventListener를 사용한 요소 추가, 삭제, 수정, 토글</h2>
			<button id="addBtn">아이템 추가</button>
			<button id="toggleBtn">리스트 토글</button>
			<ul id="myList">
				<li>
					아이템 1
					<button class="editBtn">수정하기</button>
					<button class="deleteBtn">삭제하기</button>
				</li>
			</ul>
		</div>
		<script>
			let count = 1;
			// 아이템 추가 기능
			document.getElementById("addBtn").addEventListener("click", function () {
				count++;
				// 새로운 li 요소를 생성합니다.
				let newItem = document.createElement("li");
				// 백틱(``)을 사용하여 새로운 li 요소의 내용을 설정합니다.
				// 아이템에는 수정하기와 삭제하기 버튼이 포함됩니다.
				newItem.innerHTML = `아이템 ${count} <button class="editBtn">수정하기</button><button class="deleteBtn">삭제하기</button>`;
				// "myList"라는 ID를 가진 ul 요소에 새로운 li 요소를 추가합니다.
				document.getElementById("myList").append(newItem);
			});

			// 리스트 토글 기능
			// "toggleBtn"이라는 ID를 가진 요소에 클릭 이벤트 리스너를 추가합니다.
			document.getElementById("toggleBtn").addEventListener("click", function () {
				// "myList"라는 ID를 가진 ul 요소를 선택합니다.
				let list = document.getElementById("myList");
				// 리스트의 display 스타일을 토글합니다. 보이면 숨기고, 숨겨져있으면 보이게 합니다.
				list.style.display = list.style.display === "none" ? "" : "none";
			});

			
		</script>
	</body>
</html>

2. 이벤트 위임(Event Delegation)

이벤트 리스너를 하위 요소 개별적으로 달지 않고, 상위 요소에서 하나의 리스너로 모든 하위 요소의 이벤트를 관리하는 패턴입니다. 이 패턴은 주로 동적으로 요소가 변경될 때 유용합니다.

원리

브라우저에서 이벤트는 대상 요소에서 발생하며, 이후 상위 요소로 버블링(bubbling)됩니다. 이벤트 위임을 사용하면, 상위 요소에서 하위 요소에서 발생한 이벤트를 감지할 수 있습니다.

💡 버블링(Bubbling)은 이벤트가 발생한 요소에서 시작해 DOM 트리를 따라 위로 올라가며 전파되는 이벤트 전파 방식입니다. 
   즉, 하위 요소에서 이벤트가 발생하면 그 이벤트는 상위 요소로 전파됩니다. 
   이를 통해 상위 요소에서 하위 요소의 이벤트를 감지할 수 있게 됩니다.

먼저 이벤트 전파 속성 중 버블링에 대한 개념을 알아 봅시다.

시나리오 코드 - 1 (이벤트 버블링이란)

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>Document</title>
	</head>
	<body>
		<div id="parent">
			<button id="child">Click me!</button>
		</div>

		<script>
			document.getElementById("parent").addEventListener("click", () => {
				alert("Parent Div Clicked!");
			});

			document.getElementById("child").addEventListener("click", () => {
				alert("Button Clicked!");
			});
		</script>
	</body>
</html>

결과보기

버블링 중단 하기

stopPropagation 메서드를 사용하면 이벤트 버블링을 중단할 수 있습니다.

<script>
	document.getElementById("parent").addEventListener("click", () => {
		alert("Parent Div Clicked!");
	});
	
	// document.getElementById("child").addEventListener("click", () => {
	// 	alert("Button Clicked!");
	// });
	
	// 버블링 중단하기 - 인수로 event 전달
	document.getElementById("child").addEventListener("click", (event) => {
		alert("Button Clicked!");
		event.stopPropagation(); // 버블링 중단
	});
</script>

시나리오 코드 - 2 (이벤트 위임을 활용하기 전)

이벤트 위임을 사용하지 않는 경우, 각 버튼에 개별적으로 이벤트 리스너를 추가해야 합니다.

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>Document</title>
	</head>
	<body>
		<ul id="myList">
			<li><button>버튼 1</button></li>
			<li><button>버튼 2</button></li>
			<li><button>버튼 3</button></li>
		</ul>

		<script>
			// 각 버튼에 개별적으로 이벤트 리스너를 추가
			document.querySelectorAll("#myList button").forEach((button) => {
				button.addEventListener("click", function () {
					alert("버튼 클릭!");
				});
			});
		</script>
	</body>
</html>

하지만 이벤트 위임을 사용하면, ul 요소에 하나의 이벤트 리스너만 추가하여 모든 버튼의 클릭 이벤트를 처리할 수 있습니다.

시나리오 코드 - 3 (이벤트 위임을 활용)

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>Document</title>
	</head>
	<body>
		<ul id="myList">
			<li><button>버튼 1</button></li>
			<li><button>버튼 2</button></li>
			<li><button>버튼 3</button></li>
		</ul>

		<script>
			// 각 버튼에 개별적으로 이벤트 리스너를 추가
			// document.querySelectorAll("#myList button").forEach((button) => {
			// 	button.addEventListener("click", function () {
			// 		alert("버튼 클릭!");
			// 	});
			// });

			// button 들에 부모 요소 li --> ul
			document.getElementById("myList").addEventListener("click", function (e) {
				// 1단계 확인
				// if (e.target.tagName === "BUTTON") {
				// 	alert("버튼 클릭!");
				// }

				// 2단계 인수값에 대한 이해
				console.log(e);
				console.log(e.target); // <button>
				// 버튼의 내부 텍스트(즉, 버튼에 표시되는 라벨)를 가져오기
				console.log(e.target.innerText);
				console.log("---------------");
				console.log(e.target.textContent);

				// 3단계  코드 활용
				if (e.target.innerText === "버튼 1") {
					alert("버튼 1");
				} else if (e.target.innerText === "버튼 2") {
					alert("버튼 2");
				} else if (e.target.innerText === "버튼 3") {
					alert("버튼 3");
				}
			});
		</script>
	</body>
</html>

시나리오 코드 - 4 (시나리오 1 예제 완성하기)

이벤트 위임 활용

<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<title>addEventListener</title>
		<link rel="stylesheet" href="reset.css" />
		<style>
			body {
				margin: 16px;
				display: flex;
				justify-content: center;
			}

			ul {
				list-style-type: none;
				padding: 0;
			}
			li {
				background-color: #f9f9f9;
				margin: 8px 0;
				padding: 8px;
				border: 1px solid #ccc;
				display: flex;
				justify-content: space-between;
				align-items: center;
			}
			button {
				margin-left: 8px;
			}
		</style>
	</head>
	<body>
		<div>
			<h2>EventListener를 사용한 요소 추가, 삭제, 수정, 토글</h2>
			<button id="addBtn">아이템 추가</button>
			<button id="toggleBtn">리스트 토글</button>
			<ul id="myList">
				<li>
					아이템 1
					<button class="editBtn">수정하기</button>
					<button class="deleteBtn">삭제하기</button>
				</li>
			</ul>
		</div>
		<script>
			let count = 1;
			// 아이템 추가 기능
			document.getElementById("addBtn").addEventListener("click", function () {
				count++;
				// 새로운 li 요소를 생성합니다.
				let newItem = document.createElement("li");
				// 백틱(``)을 사용하여 새로운 li 요소의 내용을 설정합니다.
				// 아이템에는 수정하기와 삭제하기 버튼이 포함됩니다.
				newItem.innerHTML = `아이템 ${count} <button class="editBtn">수정하기</button><button class="deleteBtn">삭제하기</button>`;
				// "myList"라는 ID를 가진 ul 요소에 새로운 li 요소를 추가합니다.
				document.getElementById("myList").append(newItem);
			});

			// 리스트 토글 기능
			// "toggleBtn"이라는 ID를 가진 요소에 클릭 이벤트 리스너를 추가합니다.
			document.getElementById("toggleBtn").addEventListener("click", function () {
				// "myList"라는 ID를 가진 ul 요소를 선택합니다.
				let list = document.getElementById("myList");
				// 리스트의 display 스타일을 토글합니다. 보이면 숨기고, 숨겨져있으면 보이게 합니다.
				list.style.display = list.style.display === "none" ? "" : "none";
			});

			// 아이템 수정 기능
			// "myList"라는 ID를 가진 ul 요소에 클릭 이벤트 리스너를 추가합니다.
			// 이 리스너는 이벤트 위임을 사용하여 ul의 모든 자식 li 요소에서 발생하는 클릭 이벤트를 처리합니다.
			document.getElementById("myList").addEventListener("click", function (e) {
				// 클릭된 요소의 className이 "editBtn"인 경우
				if (e.target.className === "editBtn") {
					// 수정 버튼의 부모 li 요소를 찾습니다.
					let parentLi = e.target.parentNode;
					// 사용자에게 새로운 텍스트를 입력받습니다.
					let newText = prompt("새로운 텍스트를 입력하세요", parentLi.firstChild.nodeValue.trim());
					// 사용자가 취소 버튼을 누르지 않은 경우 li 요소의 텍스트를 수정합니다.
					if (newText !== null) parentLi.firstChild.nodeValue = newText;
				}
			});

			// 아이템 삭제 기능
			// "myList"라는 ID를 가진 ul 요소에 클릭 이벤트 리스너를 추가합니다.
			// 이 리스너는 이벤트 위임을 사용하여 ul의 모든 자식 li 요소에서 발생하는 클릭 이벤트를 처리합니다.
			document.getElementById("myList").addEventListener("click", function (e) {
				// 클릭된 요소의 className이 "deleteBtn"인 경우
				if (e.target.className === "deleteBtn") {
					// 사용자에게 삭제 확인 메시지를 보여줍니다.
					if (confirm("정말 삭제하시겠습니까?")) {
						// 사용자가 확인 버튼을 누른 경우 해당 li 요소를 삭제합니다.
						e.target.parentNode.remove();
					}
				}
			});
		</script>
	</body>
</html>

아이템 수정 기능, 아이템 삭제 기능은 하나의 코드로 작성할 수 있으나 개념 확인을 위해 따로 코드를 분리 합니다.

0개의 댓글