2023. 1. 4. 18:59ㆍ프로그래밍/HTML & CSS
Event Bubbling (이벤트 버블링)
가장 깊게 중첩된 요소에 이벤트가 발생했을 때
이벤트가 위로 전달되는 것 (부모 요소들의 핸들러가 호출됨)
<body>
<form onClick="alert('form')">FORM
<div onClick="alert('div')">DIV
<p onClick="alert('p')">P</p>
</div>
</form>
</body>
<p>에 할당된 핸들러 --> <div>에 할당된 핸들러 --> <form>에 할당된 핸들러
- p 태그 클릭 : alert('p'), alert('div'), alert('form') 실행
- div 태그 클릭 : alert('div'), alert('form') 실행
- form 태그 클릭 : alert('form') 실행
<body>
<form>FORM
<div>DIV
<p>P</p>
</div>
</form>
<script>
const form = document.querySelector('form');
const div = document.querySelector('div');
const p = document.querySelector('p');
form.addEventListener('click', function () {
alert(`target=${event.target.tagName}, this=${this.tagName}`);
});
</script>
</body>
부모 요소 핸들러에서는 어디에서 이벤트가 발생했는지 알 수 있음
- p 태그 클릭 : target=p, this=form
- div 태그 클릭 : target=div, this=form
- form 태그 클릭 : target=form, this=form
event.target
실제 이벤트가 시작된 타깃 요소
event.currentTarget(this)
현재 요소, 현재 실행 중인 핸들러가 할당된 요소를 참조함
버블링 중단하기
<body>
<form onClick="alert('form')">FORM
<div onClick="alert('div')">DIV
<p onClick="event.stopPropagation(); alert('p');">P</p>
</div>
</form>
</body>
이벤트 버블링은 타깃 이벤트에서 시작해서 document 객체를 만날 때까지 각 노드에서 모두 발생함
몇몇 이벤트는 window 객체까지 거슬러 올라가기도 함
event.stopPropagation() 메서드를 호출하면 버블링이 발생하지 않음
stopPropagation()
DOM 트리를 통한 이벤트 흐름을 중지함
하지만 브라우저 기본 동작을 취소하지는 않음
preventDefault()
이벤트의 기본 동작을 방지할 수 있음
하지만 이벤트 흐름을 중지하지는 않음
Event Capturing (이벤트 캡쳐링)
제일 상단에 있는 요소에서
아래로 이벤트가 내려오는 것
이벤트의 3단계 흐름
- 캡처링 단계 : 이벤트가 하위 요소로 전파되는 단계
- 타깃 단계 : 이벤트가 실제 타깃 요소에 전달되는 단계
- 버블링 단계 : 이벤트가 상위 요소로 전파되는 단계
이벤트 흐름 확인 방법
addEventListener 의 capture 옵션을 true 로 설정하기
p 태그 클릭 시
- 캡쳐링 : HTML - BODY - FORM - DIV - P 로 진행됨
- 버블링 : P - DIV - FORM - BODY - HTML 로 진행됨
- 이벤트 흐름에서 타겟 단계는 별도로 처리되지 않음
<body>
<form>
FORM
<div>
DIV
<p>P</p>
</div>
</form>
<script>
for (element of document.querySelectorAll('*')) {
element.addEventListener('click', (event) => {
alert(`캡처링: ${event.currentTarget.tagName}`);
}, true);
element.addEventListener('click', (event) => {
alert(`버블링: ${event.currentTarget.tagName}`);
});
}
</script>
</body>
Event Delegation (이벤트 위임)
하위 요소의 이벤트를
상위 요소에 위임(제어)하는 것
<body>
<div id="buttons">
<button class="buttonClass">Click</button>
<button class="buttonClass">Click</button>
</div>
<script>
const buttons = document.getElementsByClassName('buttonClass');
for (const button of buttons) {
button.addEventListener('click', () => {
alert('clicked');
});
}
let buttonList = document.querySelector('#buttons');
let button = document.createElement('button');
button.setAttribute('class', 'buttonClass');
button.innerText = 'Click';
buttonList.appendChild(button);
</script>
</body>
div 태그 안의 두 button 요소를 클릭하면 alert('Click') 이 정상적으로 나옴
하지만 스크립트로 만든 button 요소를 클릭하면 alert('Click') 이 나오지 않음
이것은 버튼이 생기기 전에 이미 각 버튼 요소에 핸들러가 등록되었기 때문임
새로 생긴 버튼 요소에는 핸들러가 등록되어 있지 않음
해결 방법
현재는 하위 요소인 각 button 요소에서 이벤트를 제어하고 있음
새로운 하위 요소가 추가될 때마다 이벤트 리스너를 등록해주어야 함
하지만 하위 요소들이 존재하는 상위 요소에서 이벤트를 제어하면
하위 요소가 추가될 때마다 이벤트 리스너를 등록해주지 않아도 됨
const buttons = document.getElementById('buttons');
buttons.addEventListener('click', () => {
if (e.target.className === 'buttonClass') {
alert('clicked');
}
});
상위 요소인 div에 이벤트 리스너를 추가해주면 해결 가능
하위 요소에서 발생한 클릭 이벤트를 이벤트 버블링을 통해서 감지하게 됨