자바스크립트에서 이벤트가 동작되는 과정에는 두가지 방식이 있습니다.
기본적으로 이벤트 버블링으로 동작합니다.
📝이벤트 버블링
이벤트 버블링이란 하위 태그에서 이벤트가 발생할시 위에 태그로 전달하는 형태입니다.
이렇게 전달하면 이벤트가 전파되는데 밑에와 같은 코드가 있을 경우 Three → Two → One 순으로 누를시
Three 누르면 Three, Two, One이 출력이 됩니다.
Two 누르면 Two, One이 출력이 되고
One 누르면 One이 출력이 됩니다.
<!DOCTYPE html>
<head>
</head>
<body>
<div class="one" id="one">
one
<div class="two" id="two">
two
<div class="three" id="three">
three
</div>
</div>
</div>
</body>
<script>
var divs = document.querySelectorAll('div');
divs.forEach(function(div) {
div.addEventListener('click', logEvent, {
});
});
function logEvent(event) {
console.log(event.currentTarget.className);
}
</script>
이렇게 동작하는 과정을 이벤트 버블링이라고 합니다.
📝이벤트 캡처
이벤트 캡처라는 방식도 존재합니다.
이벤트 캡처란 이벤트 버블링이랑 다르게 최상위 태그에서 해당 이벤트 태그로 전파합니다.
이벤트가 전파되는데 밑에와 같은 코드가 있을 경우 Three → Two → One 순으로 누를시
Three 누르면 One, Two, Three이 출력이 됩니다.
Two 누르면 Two, One이 출력이 되고
One 누르면 One이 출력이 됩니다.
* 이벤트 버블과 반대 순서로 작동
<!DOCTYPE html>
<head>
</head>
<body>
<div class="one" id="one">
one
<div class="two" id="two">
two
<div class="three" id="three">
three
</div>
</div>
</div>
</body>
<script>
var divs = document.querySelectorAll('div');
divs.forEach(function(div) {
div.addEventListener('click', logEvent, {
capture: true // default 값은 false입니다.
});
});
function logEvent(event) {
console.log(event.currentTarget.className);
}
</script>
위와 같은 걸 고려해서 만들기 귀찮거나 할 경우에는 event.stopPropagation()을 사용하면 됩니다.
아까랑 스크립트 코드가 다르지만 이것은 이벤트 버블링이 적용된 상태입니다.
event.stopPropagation() 을 사용할 경우 전파하지않고 해당 태그만 이벤트가 작동합니다.
<!DOCTYPE html>
<head>
</head>
<body>
<div class="one" id="one" onclick="hello1()">
one
<div class="two" id="two" onclick="hello2()">
two
<div class="three" id="three" onclick="hello3()">
three
</div>
</div>
</div>
</body>
<script>
function hello1(){
event.stopPropagation();
console.log(event.currentTarget.className);
}
function hello2(){
event.stopPropagation();
console.log(event.currentTarget.className);
}
function hello3(){
event.stopPropagation();
console.log(event.currentTarget.className);
}
</script>
📝이벤트 위임
지금까지 위에서 한 행위들은 이벤트 위임을 이해하기 위함입니다. 물론 내가 원하는 방식하고 다르게 동작하는 것에 대한 파악도 가능하지만요 이벤트 위임은 바닐라 JS로 웹 앱을 구현할 때 자주 사용하게 되는 코딩 패턴입니다. 하위 요소에 각각 이벤트를 붙이지 않고 상위 요소에서 하위 요소의 이벤트들을 제어합니다.
<!DOCTYPE html>
<head>
</head>
<body>
<h1>오늘의 할 일</h1>
<ul class="itemList">
<li>
<input type="checkbox" id="item1">
<label for="item1">이벤트 버블링 학습</label>
</li>
<li>
<input type="checkbox" id="item2">
<label for="item2">이벤트 캡쳐 학습</label>
</li>
</ul>
</body>
<script>
var inputs = document.querySelectorAll('input');
inputs.forEach(function(input) {
input.addEventListener('click', function(event) {
alert('clicked');
});
});
</script>
위에 코드는 체크박스 클릭시 Alert이 노출되는 형식으로 돌아갑니다.
여기에서 회원의 권한에 따라 보여지는 체크박스 수가 달라질 경우 추가된 체크박스에 이벤트 리스너를 또 추가해야 추가된 체크박스도 또한 위와 같이 동작하게 됩니다. 이러한 걸 이벤트 위임으로 간단하게 처리할 수 있습니다.
<!DOCTYPE html>
<head>
</head>
<body>
<h1>오늘의 할 일</h1>
<ul class="itemList">
<li>
<input class="item" type="checkbox" id="item1" >
<label for="item1">이벤트 버블링 학습</label>
</li>
<li>
<input class="item" type="checkbox" id="item2">
<label for="item2">이벤트 캡처 학습</label>
</li>
</ul>
</body>
<script>
let itemList = document.querySelector('.itemList');
itemList.addEventListener('click', function(event) {
alert('clicked');
});
let createCheckBox = () => {
let li = document.createElement('li');
let input = document.createElement('input');
let label = document.createElement('label');
let labelText = document.createTextNode(' 이벤트 위임');
input.setAttribute('type','checkbox');
input.setAttribute('id','item3');
label.setAttribute('for','item3');
label.appendChild(labelText);
li.appendChild(input);
li.appendChild(label);
itemList.appendChild(li);
}
createCheckBox();
</script>
label 해당 영역을 누른 경우 2번 호출되는데 클릭시 label이 input checkbox를 호출하면서 ul범위 안에 있기 때문에 alert이 한번 호출되고 1번 ul 자체를 클릭한 거이기 때문에 1번 이런식으로 호출된다.
label을 다른 방식으로 처리하든가 분기처리를 하던가 하면 될 거 같다 (label 자체의 문제임)
생각해봤는데 이벤트 위임의 경우 ul태그가 크니까 ul태그안에 어딜 눌러도 ul에 적용되어있는 이벤트는 다 동작한다
직접 안 써봐서 모르겠는데 복잡하게 생각할 건 없는 거 같다.
출처 : https://joshua1988.github.io/web-development/javascript/event-propagation-delegation/