이벤트 핸들러
: 특정 요소에서 지정된 이벤트가 발생 했을 때 해당 이벤트를 처리하는 함수로, 연결된 이벤트 핸들러는 호출될 때 자동으로 Event 객체를 첫 번째 인자로 전달 받는다.import { useState } from "react"
function App() {
const [value, setValue] = setState("")
const handleChange = (event) => {
setValue(event.target.value)
}
return (
<div>
<input type="text" value={value} onChange={handleChange} />
</div>
)
}
export default App
부모 요소를 가지고 있는 요소에서 이벤트가 발생한 경우, 캡처링
과 타깃
, 버블링
이 차례대로 실행된다.
(1) 캡처링 단계 : <td>
를 클릭하면 이벤트가 최상위에서 시작해 아래로 전파된다.
(2) 타깃 단계 : 이벤트가 타깃 요소에 도착돼 실행된 후에
(3) 버블링 단계 : 다시 위로 전파된다.
캡처링(capturing)
: 최상위 요소
에서부터 실제 이벤트가 발생한 요소
에 이르기까지 자식 요소들을 타고 내려가 이벤트 핸들러가 있는지 검사하고, 있으면 그 이벤트 핸들러를 호출한다.버블링(bubbling)
: 실제 이벤트가 발생한 요소
부터 최상위 요소
에 이르기까지 부모 요소들을 타고 올라가 이벤트 핸들러가 있는지 검사하고, 있으면 그 이벤트 핸들러를 호출한다.몇몇 이벤트를 제외한 거의 모든 이벤트는 버블링된다. 버블링과 달리 캡처링을 이용해야 하는 경우는 흔치 않다.
target
: 이벤트가 실제 발생한 대상으로, 버블링 혹은 캡처링이 진행돼도 변하지 않는다.currentTarget
(=this
) : 버블링 혹은 캡처링 진행 중 현재 이벤트가 위치하고 있는 대상으로, 현재 실행 중인 핸들러가 할당된 요소를 참조한다.currentTarget
(=this
)는 함수(이벤트 핸들러)를 호출하는 객체이다. 위와 같은 차이로 currentTarget
은 target
과 같은 요소를 참조할 수도, 다른 요소를 참조할 수도 있다.
function App() {
const printLog = (event) => {
console.log(`currentTarget: ${event.currentTarget.id}, target: ${event.target.id}`)
}
return (
<form id="outer" onClick={printLog}>
outer
<div id="inner" onClick={printLog}>
inner
<button id="button" style={{ display: "block" }} onClick={printLog}>
target
</button>
</div>
</form>
)
}
export default App
button
클릭 시, 이벤트 버블링으로 인해 모든 요소의 클릭 이벤트 리스너에 등록된 printLog
함수가 호출된 것을 볼 수 있다. 이 때, target
은 실제 이벤트가 발생한 button
이고, currentTarget
은 button
부터 시작해 차례대로 printLog
함수가 호출된 부모 요소가 된다.
<button>
클릭 했을 때 아래의 순서로이벤트 버블링
이 발생한다.
1.<button>
에 할당된onClick
핸들러가 동작한다.
2. 바깥의<div>
에 할당된 핸들러가 동작한다.
3. 그 바깥의<form>
에 할당된 핸들러가 동작한다.
4.document
객체를 만날 때까지, 각 요소에 할당된onClick
핸들러가 동작한다.
function Bubbling() {
const chageBgToYellow = (event) => {
console.log(
`currentTarget: ${event.currentTarget.tagName}, target: ${event.target.tagName}`
);
event.target.style.backgroundColor = 'yellow';
};
return (
<form onClick={chageBgToYellow}>
Form
<div>
DIV
<p>P</p>
</div>
</form>
);
}
export default Bubbling;
<p>
-> <div>
-> <form>
순으로 클릭했을 때 currentTarget
과 target
의 태그명은 위와 순서와 같다.
이벤트 핸들러는 <form>
에만 할당돼있지만 이 핸들러에서 <form>
안의 모든 요소에서 발생하는 클릭 이벤트를 ‘잡아내고(catch)’ 있다. 클릭 이벤트가 어디서 발생했든 상관없이 <form>
요소까지 이벤트가 버블링 되어 핸들러를 실행시키기 때문이다.
function App() {
const printLog = (event) => {
event.stopPropagation()
console.log(`currentTarget: ${event.currentTarget.id}, target: ${event.target.id}`)
}
return (
<form id="outer" onClick={printLog}>
outer
<div id="inner" onClick={printLog}>
inner
<button id="button" style={{ display: "block" }} onClick={printLog}>
button
</button>
</div>
</form>
)
}
export default App
이벤트 핸들러에 event.stopPropagation()
를 사용해 이벤트 전파를 막은 상태에서 button
클릭 시, 이전과 달리 부모 요소에서는 console.log가 실행되지 않는다.
하지만 이 방법은 추후에 문제가 될 수 있는 상황을 만들어낼 수 있으므로 꼭 필요한 경우를 제외하곤 버블링을 막으면 안 된다.
문제가 발생할만한 시나리오
1. 중첩 메뉴에서 서브메뉴에 해당하는 요소에서 클릭 이벤트를 처리하도록 하고 상위 메뉴의 클릭 이벤트 핸들러는 동작하지 않도록stopPropagation
을 적용한다.
2. 이용자들이 웹 페이지에서 어디를 클릭했는지 분석하기 위해, window내에서 발생하는 클릭 이벤트 전부를 감지하기 위해 최상위 요소에 클릭 이벤트 핸들러를 연결한다.
3.stopPropagation
로 버블링을 막아놓은 영역에선 2의 코드가 동작하지 않게 된다.
preventDefault(), stopPropagation(), stopImmediatePropagation() 비교
https://velog.io/@line_jeong32/%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EB%A9%94%EC%84%9C%EB%93%9C
function App() {
const printLog = (event) => {
const {target, currentTarget} = event
if(target !== currentTarget) return
console.log(`currentTarget: ${currentTarget.id}, target: ${target.id}`);
};
return (
<form id="outer" onClick={printLog}>
outer
<div id="inner" onClick={printLog}>
inner
<button id="button" style={{ display: "block" }} onClick={printLog}>
button
</button>
</div>
</form>
);
}
export default App;
이벤트 핸들러가 호출 됐을 때 target
과 currentTarget
이 일치하지 않을 시, return 시켜버리면 button
클릭 시, target
과 currentTarget
이 일치하지 않는 부모 요소에서는 console.log가 실행되지 않는다.
참고 사이트 및 블로그