이벤트버블링
아래의 코드는 각 태그에 해당하는 박스가 있습니다.(body> div > ul >li 순)
각 박스의 클릭이벤트가 발생하면 콘솔창에 기록됩니다.
<body>
body
<div id="div">
div
<ul id="ul">
ul
<li id="li">li</li>
</ul>
</div>
<script>
const d = document.getElementById("div");
const u = document.getElementById("ul");
const l = document.getElementById("li")
document.body.addEventListener('click', ()=>{
console.log('1.body');
})
d.addEventListener('click', ()=>{
console.log('2.div');
})
u.addEventListener('click', ()=>{
console.log('3.ul');
})
l.addEventListener('click', ()=>{
console.log('4.li');
})
</script>
</body>
body{
border : 1px solid;
padding:30px;
background-color:white;
}
div{
margin:30px;
padding:30px;
border : 1px solid;
background-color: blue;
}
ul{
margin:30px;
padding:30px;
border : 1px solid;
background-color: green;
}
li{
margin:30px;
padding:30px;
border : 1px solid;
background-color: pink;
}
위 코드의 결과화면입니다.
li를 클릭시 , 아래와 같은 결과가 나옵니다.
즉 li > ul > div> body 순으로 콘솔창에 찍힙니다.
다음으로 ul을 클릭해보겠습니다.
ul을 클릭할때도 마찬가지로 자기자신과 부모의 요소들이 찍힙니다.
이러한 현상을 이벤트 버블링(Event Bubblilng)이라고 합니다.
자식요소에서 발생한 이벤트가, 최종 부모까지 전파됩니다. 즉 하위에서 상위요소로 전달됩니다.
그림으로 보면 아래와같습니다.
대부분의 이벤트는 버블링으로 전파되지만 이벤트 버블링에도 예외는 있습니다.
- focus
- blur
- mouseenter
- mouseleave
위의 이벤트는 버블링이 적용안됩니다. 저 4가지의 이벤트가 발생하였을때 상위 이벤트에서 캐치할려면 다음과 같이 사용
- focus -> focusin
- blur -> focusout
- moseenter -> mouseover
- mouseleave -> mouseout
예시를 통해 보겠습니다.
body 안에 div안에 input이 있습니다.
<body>
<div id ="div">
div
<input id="txt">
</div>
<script>
const d=document.getElementById("div");
const txt=document.getElementById("txt");
document.body.addEventListener("focus", ()=>{
console.log("focus -1. body");
})
d.addEventListener("focus",()=>{
console.log("focus -2. div");
})
txt.addEventListener("focus", ()=>{
console.log("focus -3. input");
})
document.body.addEventListener("blur", ()=>{
console.log("blur -1. body");
})
d.addEventListener("blur", ()=>{
console.log("blur -2. div");
})
txt.addEventListener("blur", ()=>{
console.log("blur -2. input");
})
</script>
</body>
위의 화면에서 input을 클릭해 focus 이벤트를 발생시키면 어떻게 될까요?
input에 focus를 한 후 콘솔창을 통해 확인해보면 콘솔창에는 foucs -3. text만 출력합니다.
즉 위에서 보았던 이벤트 버블링 input -> div -> body 로 이벤트 전파가 안됩니다.
위의 코드에서 focus를 focusin으로 blur을 focusout으로 변경해보겠습니다.
<body>
<div id ="div">
div
<input id="txt">
</div>
<script>
const d=document.getElementById("div");
const txt=document.getElementById("txt");
document.body.addEventListener("focusin", ()=>{
console.log("focusin -1. body");
})
d.addEventListener("focus",()=>{
console.log("focusin -2. div");
})
txt.addEventListener("focusin", ()=>{
console.log("focusin -3. input");
})
document.body.addEventListener("focusout", ()=>{
console.log("focusout -1. body");
})
d.addEventListener("focusout", ()=>{
console.log("focusout -2. div");
})
txt.addEventListener("focusout", ()=>{
console.log("focusout -2. input");
})
</script>
</body>
변경한 코드이고 똑같이 input에 focus이벤트를 발생시켜보겠습니다.
위의 결과처럼 focusin이벤트를 발생시키면 이벤트 버블링이 잘 발생함을 확인할 수 있습니다.
이번에는 이벤트 버블링을 막아보겠습니다.
- event.stopPropagation() 을사용하면 이벤트 버블링을 막을 수 있습니다.
- 이벤트 객체를 인수로 받아서 처리하면됩니다.
txt에만 적용하면 됩니다. 인수로 event를 받고 event.stopPropagation()을 사용합니다.
확인을 해보면 아래처럼 이벤트 버블링이 발생하지 않습니다.
그러나 이벤트버블링을 막아야 하는 경우는 드물어서, 자주 사용되지는 않습니다.
이벤트 위임
- 자신에게 발생한 이벤트를 다른 요소에서 처리하는 것
- 이벤트 버블링을 활용하여 이벤트 위임을 할 수 있습니다.
아래의 코드는 li가 클릭되면 배경이 변경되는 코드입니다.
<body>
<div id="div">
<ul id="list">
<li id="red" class="on">Red</li>
<li id="blue">Blue</li>
<li id="green">green</li>
<li id="pink">Pink</li>
</ul>
</div>
<script>
const list = document.getElementById("list")
const colors = list.children;
function clickHandler(event){
for(c of colors){
c.classList.remove("on");
}
event.target.classList.add("on");
}
document.getElementById("red").addEventListener("click", clickHandler);
document.getElementById("blue").addEventListener("click", clickHandler);
document.getElementById("green").addEventListener("click", clickHandler);
document.getElementById("pink").addEventListener("click", clickHandler);
</script>
</body>
#div{
border: 1px solid;
background-color: green;
}
#list{
margin:30px;
padding:30px;
border: 1px solid;
background-color: chocolate;
}
li{
margin:30px;
padding:30px;
border : 1px solid;
background-color: yellow;
}
ul li.on{
background-color: red !important;
}
이벤트위임 사용하는 이유
- 문제 : 만약 li가 100개로 늘어나고, 100개에 이벤트와 이벤트핸들러를 등록해줘야 한다면?
- 해결 : 부모 ul에 이벤트를 위임
- 이벤트 등록 1줄로 해결, li개수가 바뀌어도 코드 수정 불필요
- 위의 방법이 가능한 이유는 이벤트 버블링 때문이다. li에서 발생한 이벤트가 상위요소로 전파될 때, 부모 ul에 등록된 이벤트 핸들러가 실행되기 때문
document.getElementById("list").addEventListener("click", clickHandler);
여기서 li에 a태그가 추가되면 어떻게 될까요?
여기서도 문제가 발생합니다.
문제 : <a>에 inline요소로 처리된 배경색과 <li>에 블록요소로 처리된 배경색이 각각 따로 중복 장독됩니다.
해결 : currentTarget, target 사용
- currentTarget은 항상 ul을 가리킵니다. 즉 이벤트 & 이벤트핸들러가 등록된 요소
- target은 클릭된 요소를 가리킵니다. 즉 실제 이벤트를 발생시키는 요소 입니다.
clickHandler함수를 수정해줍니다.
function clickHandler(event){
console.log("target", event.target);
console.log("currentTarget", event.currentTarget);
let target = event.target;
if(target.tagName==='A'){
target=target.parentElement; // 부모요소인 li로 이벤트타겟을 할당
}
else if(target===event.currentTarget){ //클릭한게 ui이면
return;
};
for(c of colors){
c.classList.remove("on");
}
event.target.classList.add("on");
}
'프론트엔드 > javascript' 카테고리의 다른 글
자바스크립트 - 자료형과 형 변환 (0) | 2023.05.20 |
---|---|
자바스크립트 - 변수와 상수 (0) | 2023.05.20 |
자바스크립트 DOM & EVENT #5.2 이벤트 핸들러(Event Handler) (0) | 2023.04.30 |
자바스크립트 DOM & EVENT #5.1 이벤트 핸들러(Event Handler) (0) | 2023.04.27 |
자바스크립트 DOM&EVENT #4 CSS style, class 제어 (0) | 2023.04.26 |