Event Propagation
이벤트는 전파(Propagation) 가능
Bubbling & Capturing
event객체.stopPropagation();
Code
<!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>
<style>
#fruits {
padding: 50px;
border: 2px solid skyblue;
}
#fruits>li {
background: yellowgreen;
margin-bottom: 10px;
font-size: 20px;
font-weight: bold;
text-align: center;
}
</style>
<body>
<!-- ul#fruits>li*3 -->
<ul id="fruits">
<li>Apple</li>
<li>Banana</li>
<li>Grape</li>
</ul>
<script>
const $fruits = document.getElementById('fruits');
function m(event) {
console.log('ul에 클릭 이벤트가 발생!');
console.log('이벤트 핸들러가 붙은 타겟 : ' + event.currentTarget);
console.log('실제 이벤트가 발생한 요소 : ' + event.target);
}
$fruits.addEventListener('click', m);
// ul태그인 fruits에만 이벤트를 걸었는데 li태그를 클릭하면 li태그에서도 이벤트가 발생
function apple(event) {
console.log('apple에 클릭 이벤트가 발생 ')
// li의 첫번째 태그에 클릭을 하게 되면 apple()이 먼저 실행되고 이후 부모에 걸린 이벤트도 실행됨
// 이벤트 전파를 중단하고 싶을 때 (버블링을 방지 )
event.stopPropagation();
// apple함수 이후 m함수가 실행되어 이벤트가 전파되는 것을 방지하기 위해
// $fruits에 걸린 이벤트에 전파되지 않음
}
$fruits.firstElementChild.addEventListener('click', apple);
</script>
</body>
</html>
Event Propagation2
이벤트는 전파된다는 성질을 이용
특정 요소에서만 이벤트가 발생하도록 하는 로직 구현
matches() 함수를 사용하여 특정 조건에서 이벤트가 실행되도록 구현
Code
<!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>
<style>
#fruits {
list-style: none;
padding: 0;
font-weight: bold;
font-size: 30px;
}
#fruits li {
width: 100px;
cursor: pointer;
}
#fruits .active {
color: blue;
text-decoration: underline;
text-align: center;
}
</style>
</head>
<body>
<ul id="fruits">
<li id="apple" class="active">APPLE</li>
<li id="banana">BANANA</li>
<li id="grape">GRAPE</li>
<li id="strawberry">STRAWBERRY</li>
<li id="tomato">TOMATO</li>
</ul>
<div>선택된 과일 : <em class='msg'>apple</em></div>
<br>
# 새로운 과일 추가 :
<input type="text" class="text-box">
<button id="add">추가</button>
<script>
const $fruits = document.getElementById('fruits');
const $msg = document.querySelector('.msg');
const $liList = [...$fruits.children];
// 이벤트 핸들러 함수
function activate(event) {
// 이벤트 발생 타깃이 특정 요소인지 검증
// li에만 이벤트가 발생하도록
if (!event.target.matches('#fruits > li')) {
// matches() : 선택자를 사용하여 이벤트 발생 요소를 검증
// fruits안에 li가 매칭이 안된다면 이벤트가 발생하면 안되는 곳
console.log('여기는 이벤트가 발생하면 안됩니당~');
return;
// 이벤트 강제 종료
} else {
// console.log('여기는 이벤트가 발생해도 됨');
for (let $target of $liList) {
// li요소들이 하나씩 들어옴
// toggle 메서드의 두번째 매개값으로 논리값을 전달할 수 있고
// 해당 논리값이 true로 판명나면 지정한 클래스를 추가하고
// false로 판명나면 지정한 클래스를 삭제
$target.classList.toggle('active', $target === event.target);
// toggle은 있으면 삭제해주고 없으면 생성해줌
// toggle은 조건을 걸어줄 수 있음
}
$msg.textContent = event.target.textContent;
// msg태그의 text부분을 현재 이벤트가 발생한 부분의 텍스트를 가져와서 넣음
}
}
// ul에 event를 등록 (li가 많아서 하나씩 걸어주기 귀찮음 )
$fruits.addEventListener('click', activate);
// 동적으로 과일 추가 기능
const $btn = document.getElementById('add');
const $textBox = document.querySelector('.text-box');
function cl(event) {
const $newLi = document.createElement('li');
// li요소 생성
$newLi.textContent = $textBox.value;
// 새롭게 생성한 li요소에 textbox에 저장되어 있는 값을 넣어줌
$newLi.setAttribute('id', $textBox.value.toLowerCase());
// 새롭게 만든 li요소에 id 추가
$fruits.appendChild($newLi);
$textBox.value = '';
// 다시 입력받을 수 있도록 비워줌
$liList.push($newLi);
// 새롭게 추가된 li에도 이벤트를 부여하기 위해 위에 있는 $liList추가
// 중요
}
// 버튼에 event 등록
$btn.addEventListener('click', cl);
// li가 많기 때문에 각각 event를 걸어주는 것을 비효율적
</script>
</body>
</html>
Propagation Stop
버블링으로 인해 반복 event 실행을 막아주기 위해서는 event.stopPropagation();를 통해서 버블링 차단
Capturing
event객체.preventDefault();
Code
<!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>
<style>
.root {
width: 500px;
height: 500px;
background: blue;
}
.parent {
width: 50%;
height: 50%;
/* root의 50% */
background: orange;
}
.child {
width: 50%;
height: 50%;
/* parent의 50% */
background: chartreuse;
}
</style>
<body>
<a href="https://www.naver.com">네이버로 이동</a>
<br>
<div class="root">
<div class="parent">
<div class="child"></div>
</div>
</div>
<script>
function a(event) {
alert('root 클릭 확인');
}
function b(event) {
alert('parent 클릭 확인');
// event.stopPropagation();
// parent에서만 event가 실행될 수 있도록 함
}
function c(event) {
alert('child 클릭 확인');
// event.stopPropagation();
// child에서만 event가 실행될 수 있도록 함
}
document.querySelector('.root').addEventListener('click', a, true);
document.querySelector('.parent').addEventListener('click', b, true);
// addEventListener의 매개값으로 true를 주게 되면 capturing(버블링 반대) 실행
document.querySelector('.child').addEventListener('click', c);
// a태그에 event 걸기
const $link = document.querySelector('a');
function d(event) {
if (!confirm('정말 이동하시겠어요?')) {
// 사용자가 취소를 누르게 되면 현재 페이지에 머무르도록
event.preventDefault();
// 태그의 고유 기능을 막음
}
}
$link.addEventListener('click', d);
</script>
<script>
</script>
</body>
</html>