๋ชจ๋ element๋ node๋ฅผ ์์ํ๊ณ ๋ชจ๋ node๋ Event๋ฅผ ์์ํ๋ค. ์ฆ, ๋ชจ๋ ์๋ฆฌ๋จผํธ๋ eventTarget์ด๋ค.
-
์์ฃผ ์ฌ์ฉ๋๋ ์ด๋ฒคํธ
mouse : click, mousemove, contextmenu
window : resize, scroll
form : submit, reset, change, focus, blur
๋ค์ํ ์ด๋ฒคํธ ์ข ๋ฅ๋ค MDNevent.preventDefault()
: ์ด๋ฒคํธ๊ฐ ๊ฐ์ง๊ณ ์๋ ๊ธฐ๋ณธ๊ธฐ๋ฅ ์ทจ์ (์์ฃผ ์ฌ์ฉ๋๋ ์์ : submit์ reload๋๋ ๊ฒ์ ๋ฐฉ์งํจ) -
3๊ฐ์ง event Methods
EventTarget.addEventListener()
: EventTarget์ ํน์ ์ด๋ฒคํธ ์ฒ๋ฆฌ๊ธฐ(handler)๋ฅผ ๋ฑ๋ก
EventTarget.removeEventListener()
: EventTarget์ ์ฃผ์ด์ง ์์ ๊ธฐ ์ ๊ฑฐ
EventTarget.dispatchEvent()
: EventTarget์ ํน์ ์ด๋ฒคํธ๋ฅผ ๋ณด๋
(dispatchEvent๋ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ๋๊ธฐ์ ์ผ๋ก ํธ์ถํ ์ ์๋ค. ์๋ฅผ ๋ค์ด ํด๋ฆญํ์ง ์์๋ dispatchEvent์ ํด๋ฆญ์ด๋ฒคํธ๋ฅผ ๋ฑ๋กํ๋ฉด, ํด๋ฆญ์ด๋ฒคํธ๊ฐ ์คํ๋๋ ๊ฒ๊ณผ ๊ฐ์ ํจ๊ณผ๋ฅผ ๋ผ ์ ์๋ค.) -
Bubbling And Capturing
๋ฒ๋ธ๋ง, ์บก์ฒ๋ง MDN
์ด๋ฒคํธ ์บก์ฒ๋ง : ๋ฐ์ธ๋ฉ ๋์ด์๋ ๋ถ๋ชจ๋ถํฐ ์ด๋ฒคํธ ์ฒ๋ฆฌ๊ธฐ๊ฐ ๋ถ๋ฌ์ ๋ด๋ ค์ ธ ์ค๋ ๊ฒ (์บก์ฒ๋ง ๋จ๊ณ์์ ๋ฌด์ธ๊ฐ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๋ ์ผ์ ๊ฑฐ์ ์๋ค๊ณ ํฉ๋๋ค.)
์ด๋ฒคํธ ๋ฒ๋ธ๋ง : ์บก์ฒ๋ง๊ณผ ๋ฐ๋๋ก ์ด๋ฒคํธ๊ฐ ์ผ์ด๋ ๋์๋ถํฐ ์ด๋ฒคํธ๊ฐ ๋ฐ์ธ๋ฉ ๋์ด์๋ ๋ถ๋ชจ๊น์ง ์ฌ๋ผ๊ฐ๋ ๊ฒ (์์ ๋ถ๋ชจ์ ์ด๋ฒคํธ๋ฅผ ํธ์ถํฉ๋๋ค.)
//sample code - ๊ฐ div์ ํด๋ฆญ์ด๋ฒคํธ๋ฅผ ๋ฑ๋กํ๊ณ , ์ด๋ฒคํธ ๋ฐ์์, currentTarget๊ณผ target์ ์ฝ์์ ์ถ๋ ฅ
<body>
<div class="parent">
Parent
<div class="middle">
Middle
<div class="child">
Child
</div>
</div>
</div>
<script>
const parent = document.querySelector(".parent");
const middle = document.querySelector(".middle");
const child = document.querySelector(".child");
parent.addEventListener("click", ()=>{console.log("I am parent");console.log("event.currentTarget",event.currentTarget); console.log("event.target",event.target)})
middle.addEventListener("click", ()=>{console.log("I am something in between");console.log("event.currentTarget",event.currentTarget); console.log("event.target",event.target)})
child.addEventListener("click", ()=>{console.log("I am child");console.log("event.currentTarget",event.currentTarget); console.log("event.target",event.target)})
</script>
</body>
-
Comparison - event.target & event.currentTarget
event.target :์ค์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ ์์น. ์ฆ, ๋ด๊ฐ ํด๋ฆญํ ์์
event.currentTarget : ์ด๋ฒคํธ๊ฐ ๋ฐ์ธ๋ฉ ๋์ด์๋ ์์น(this๊ฐ ๊ฐ๋ฆฌํค๋ ๊ฒ). ์๋ฅผ ๋ค๋ฉด ์์์ ๋ถ๋ชจ ์์๊ฐ ๋ ์๋ ์๋ค.
๋ฒ๋ธ๋ง ๋จ๊ณ์์ ์์ ๋ถ๋ชจ์ ์ด๋ฒคํธ๋ฅผ ํธ์ถํ์ง ์๊ธฐ ์ํด์๋ stopPropagation()
๋ฅผ ์ฌ์ฉํ๋ค. ํ์ง๋ง, ์ด๊ฒ์ ์ด๋ฒคํธ ์์ฒด๋ฅผ ๋ฌด์ํ๋ ๊ฒ์ด๋ฏ๋ก ์ถํ ํ๋ก์ ํธ๊ฐ ๋ณต์กํด์ง ๊ฒฝ์ฐ ๋ฌธ์ ๊ฐ ๋ ์ ์์ผ๋ ์๋์ ๊ฐ์ด ์ฒ๋ฆฌํด์ฃผ๋ ๋ฐฉ๋ฒ์ด ๋ ์ข๋ค.
(event) => {
if (event.target !== event.currentTarget) {
return;
}
};
- ์ด๋ฒคํธ ์์ (Delegation)
์ด๋ฒคํธ ์์์ ๋น์ทํ ๋ฐฉ์์ผ๋ก ์ฌ๋ฌ ์์๋ฅผ ๋ค๋ฃฐ๋ ์ฌ์ฉ๋๋ค. ์์์ ํ ๋นํ ์ด๋ฒคํธ๋ฅผ ๋ถ๋ชจ์๊ฒ ์์ํ์ฌ ์์์์ ๊ฐ๊ฐ์ ์ด๋ฒคํธ๊ฐ ์ ์ฉ๋ ํจ๊ณผ๋ฅผ ์ป๋๋ค.
์๋์ ์์๋ ulํ๊ทธ ์๋ ๊ฐ liํ๊ทธ๋ฅผ ์ ํ์ ๋ฐฐ๊ฒฝ์์ด ๋ฐ๋๊ฒ ํ๋ ๊ฒฝ์ฐ์ด๋ค.
<style>
.selected {
background-color: darkgreen;
}
</style>
<ul class="ul">
<li>One</li>
<li>Two</li>
<li>Three</li>
<li>Four</li>
<li>Five</li>
</ul>
const ul = document.querySelector("ul");
ul.addEventListener("click", (event) => {
if (event.target.tagName == "LI") {
event.target.classList.add("selected");
}
});
currentTarget๊ณผ target์ ์ด์ฉํ์ฌ ๊ฐ ์๋ฆฌ๋จผํธ์ ์ ๋ณด๋ฅผ ๋ฐ์์ฌ ์ ์๋ค. ์ด๋ฅผ ํ์ฉํ์ฌ ๊ฐ ๊ฐ๋ณ ๋ฆฌ์คํธ์๊ฒ ์ด๋ฒคํธ๋ฆฌ์ค๋๋ฅผ ๋ฑ๋กํ ๊ฒ๊ณผ ๊ฐ์ ํจ๊ณผ๋ฅผ ๋ผ ์ ์๊ฒ ๋๋ ๊ฒ์ด๋ค.
์๋ ์์๋ค์ React์ postCSS๋ฅผ ์ฌ์ฉํ ์์์
๋๋ค.
(postCSS ์ฌ์ฉ์, ์ฅ์ ์ ๋ชจ๋ํ๊ฐ ๊ฐ๋ฅํฉ๋๋ค. app.jsx
์ cssํ์ผ๋ก app.module.css
๋ฅผ ๋ง๋ค๊ณ app.jsx
์์ import styles from "./app.module.css"
ํ์ฌ className={styles.ํด๋์ค์ด๋ฆ}
์ ํ์์ผ๋ก ์์ฑํฉ๋๋ค. postCSS๋ฅผ ์ฌ์ฉํ๋ฉด ์คํ๋ ๋, ๊ฐ ํ์ผ ๋จ์๋ก BEM ํ์์ ํด๋์ค ์ด๋ฆ์ด ์์์ ์์ฑ์ด ๋๊ธฐ ๋๋ฌธ์ ํด๋์ค ์ด๋ฆ์ด ๊ฒน์น๋ ๊ณ ๋ ค๋ฅผ ํ์ง ์์๋ ๋ฉ๋๋ค. ์ฆ, ๊ฐ๋ณ ํ์ผ์์๋ง ํด๋์ค ์ด๋ฆ์ด ๊ฒน์น์ง ์๊ฒ ํด์ฃผ๋ฉด ๋๊ธฐ ๋๋ฌธ์ ํด๋์ค ์ด๋ฆ์ ๋ํ ๊ณ ๋ฏผ์ด ์ค์ด๋ค ์ ์์ต๋๋ค~!)
- ์ ํ ์์์ ๋ฐ๋ฅธ ์ ์ฒด์ ์ธ ๋ ์ด์์ ๋ณ๊ฒฝ
๋ฆฌ์กํธ์์ state์ ์ํ์ ๋ฐ๋ผ ์กฐ๊ฑด๋ฌธ์ ์ฌ์ฉํด ์ปดํฌ๋ํธ๋ฅผ ๋ณด์ฌ์ค ์ ์๋ค. ์๋์ ์์๋ selectedVideo๊ฐ ์์ ๊ฒฝ์ฐ VideoDetail ์ปดํฌ๋ํธ๋ฅผ ๋ณด์ฌ์ค๋ค. ๋ํ, VideoList์ prop
์ผ๋ก display์ ์ด๋ค ์ ๋ณด๋ฅผ ๋ด๋ ค์ค ์ง ์กฐ์ ํ ์ ์๋ค.
return (
//...
<section>
{selectedVideo && (
<div className={styles.detail}>
<VideoDetail video={selectedVideo} />
</div>
)}
<div className={styles.list}>
<VideoList
videos={videos}
onVideoClick={selectVideo}
display={selectedVideo ? "list" : "grid"}
/>
</div>
</section>
);
๋ด๋ ค์จ display ์ ๋ณด์ ๋ฐ๋ผ ํด๋์ค์ด๋ฆ์ ์ ํํ ์ ์๋ค. ํด๋์ค ์ด๋ฆ์ container์ displayType์ ๋ค์ค์ผ๋ก ๋ฃ์ด์ฃผ์ด ์ถํ ์ด๋ค CSS๋ฅผ ๋ณด์ฌ์ค ์ง ์ค์ ํ ์ ์๋ค.
const Video = memo(({ ... , display }) => {
const displayType = display === "list" ? styles.list : styles.grid;
return (
<li
className={`${styles.container} ${displayType}`}
onClick={() => onVideoClick(video)}>)})
.container.grid {
width: 50%;
}
.container.list {
width: 100%;
}
- ์กฐ๊ฑด(loading) ์ฌ๋ถ์ ๋ฐ๋ผ ์์์ ๋ทฐ์ํ ๋ณ๊ฒฝ
loading์ state๋ก ๊ด๋ฆฌํ์ฌ loading ์ฌ๋ถ์ ๋ฐ๋ผ ๋ฒํผ์ด ๋ณด์ฌ๊ฑฐ๋, ๋ก๋ฉ์คํผ๋๊ฐ ๋ณด์ฌ์ง๋ ๊ฒ์ ๊ด๋ฆฌํ ์ ์๋ค.
!loading์ธ ๊ฒฝ์ฐ ์ค์ ํ name ์์์ ๋ฐ๋ผ ๋ฒํผ ์์ pink, grey ์ค ์ค์ ์ด ๊ฐ๋ฅํ๋ค.
{
!loading && (
<button
className={`${styles.button} ${name ? styles.pink : styles.grey}`}
onClick={onButtonClick}
>
{name || "No File"}
</button>
);
}
{
loading && <div className={styles.loading}></div>;
}
- ์ ํ ์์์ ๋ฐ๋ผ ๊ฐ๋ณ ๋์์ธ ๋ณ๊ฒฝ
<select name="theme">
์์ํ๊ทธ์ธ <option>
์์ dark, light, colorful์ผ๋ก theme๋ณ๋ก ์ ํ์ด ๊ฐ๋ฅํ๊ฒ ํ์๋ค.
์ด๋, ์ ํ๋ theme๋ณ๋ก switch๋ฌธ์ ์ฌ์ฉํ์ฌ ํด๋์ค ์ด๋ฆ์ ๋ฐํํ ์ ์๋๋ก ์ค์ ํ์๋ค.
์ค์ ๋ ๊ฐ์ ๋ฐ๋ผ ํด๋์ค ์ด๋ฆ์ ์ถ๊ฐ๋๋ฉฐ ๊ฐ ์ด๋ฆ์ ๋ง๋ css ์ค์ ์ ๋ฐ๋ผ ๋์์ธ ์กฐ์ ์ด ๊ฐ๋ฅํ๋ค. ์ ํ์ ๊ฒฝ์ฐ๊ฐ 2๊ฐ์ง์ธ ๊ฒฝ์ฐ ์์ ์์์ ๊ฐ์ด ์ผํญ์ฐ์ฐ์๋ฅผ ์ด์ฉํ์ฌ ๊ฐ๋จํ if๋ฌธ์ ๊ตฌ์ฑํ์๋ค๋ฉด, ์ ํ์ ์์๊ฐ 3๊ฐ์ง ์ด์์ด ๋๋ ๊ฒฝ์ฐ switch๋ฌธ์ผ๋ก ๊ตฌ์ฑํด์ฃผ๋ฉด ๋๋ค.
return (
<li className={`${styles.card} ${getStyles(theme)} `}>
<img src={url} alt="avatar" className={styles.avatar} />
</li>
);
})
function getStyles(theme) {
switch (theme) {
case "dark":
return styles.dark;
case "light":
return styles.light;
case "colorful":
return styles.colorful;
default:
throw new Error(`unknown theme: ${theme}`);
}
}
element MDN
๋ค์ํ ์ด๋ฒคํธ ์ข
๋ฅ๋ค MDN
EventTarget Method MDN
์ด๋ฒคํธ ๋ฒ๋ธ๋ง, ์บก์ฒ๋ง
๋๋ฆผ์ฝ๋ฉ ๊ฐ์๋ด์ฉ์ ๋ฐํ์ผ๋ก ์์ฑํ์์ต๋๋ค.