[JS] ์ด๋ฒคํธ ์ ํ(์บก์ฒ๋ง, ๋ฒ๋ธ๋ง) / ์ด๋ฒคํธ ์์ / ๋ฐ์ดํฐ ์์ฑ
์ค์ต์ฉ CodePen
์ด๋ฒคํธ ๋ฒ๋ธ๋ง/์บก์ฒ๋ง, `stopPropagation()`, `preventDefault()`๊น์ง ๋ชจ๋ ์ค์ตํ ์ ์๋๋ก ์์ฑ
See the Pen Learn Event Propagation ์ด๋ฒคํธ ์ ํ ์ค์ต by ColorFilter (@colorfilter) on CodePen.
์ด๋ฒคํธ ์ ํ | Event Propagation
๋ฒ๋ธ๋ง Bubbling / ์บก์ฒ๋ง Capturing
์ ์ฝ๋ํ ์์ ์์ ํ๋จ์ ์๋ span
ํ๊ทธ๋ฅผ ํด๋ฆญํ๋ฉด โ "span ์์ญ" → โ "p ์์ญ" → โ "div" ์์ญ ์์ผ๋ก ์ฝ์์ด ์ฐํ๋ค. span ์์ญ์ ํด๋ฆญํ ์๊ฐ ๋ธ๋ผ์ฐ์ ๊ฐ ์ด๋ฒคํธ๋ฅผ ๊ฐ์งํด์ ์ต์์์ ์๋ ์์๊น์ง ์ด๋ฒคํธ๊ฐ ์ ๋ฌ๋๋ ๊ฒ. ์ด๊ฒ์ ์ด๋ฒคํธ ๋ฒ๋ธ๋ง์ด๋ผ๊ณ ๋ถ๋ฅธ๋ค.
์ด๋ฒคํธ ์บก์ฒ๋ง์ ๋ฒ๋ธ๋ง ๋ฐ๋๋ฐฉํฅ์ผ๋ก ์งํ๋๋ค. addEventListener()
3๋ฒ์งธ ์ธ์์ true
๋ฅผ ๋ช
์ํ๋ฉด ๋๋ค. ์ด๋ { capture: true }
๋ฅผ ๋ช
์ํ ๊ฒ๊ณผ ๊ฐ๋ค. ๊ธฐ๋ณธ ๊ฐ์ { capture: false }
๋ค.
ํ๋จ์ span
ํ๊ทธ๋ฅผ ํด๋ฆญํ๋ฉด ์ฝ์์ โ "div ์์ญ" → โ "p ์์ญ" → โ "span" ์์ญ ์์ผ๋ก ์ฝ์์ด ์ฐํ๋ค. <span>
ํ๊ทธ๋ฅผ ํด๋ฆญํ ์๊ฐ ์ต์์ ์์ <html>
ํ๊ทธ๋ถํฐ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ <span>
ํ๊ทธ๊น์ง ๋ด๋ ค๊ฐ๋ฉฐ ํ์ํ๊ธฐ ์์ํ๋ค. ํ์ํ๋ ๊ณผ์ ์์ ์ค๊ฐ์ ๊ฑฐ์น๋ ์์์ ํ ๋น๋ ํธ๋ค๋ฌ๊ฐ ์คํ๋ผ์ ์ฝ์์ด ์ฐํ๋ ๊ฒ.
โถ ์ด๋ฒคํธ ๋ฒ๋ธ๋ง(Event Bubbling) : ์๋ → ์
`target` (์ด๋ฒคํธ๊ฐ ๋ฐ์ํ ์์) → `...` → `body` → `html`
โท ์ด๋ฒคํธ ์บก์ฒ๋ง(Event Capturing): ์ → ์๋
`html` → `body` → `...` → `target` (์ด๋ฒคํธ๊ฐ ๋ฐ์ํ ์์)
โธ `addEventListener()` 3๋ฒ์งธ ํ๋ผ๋ฏธํฐ๊ฐ ์บก์ฒ๋ง ์ฌ๋ถ ๊ฒฐ์
์ด๋ฒคํธ ๋ฒ๋ธ๋ง : `{ capture: false }` ํน์ `false` ํน์ ์๋ฌด๊ฒ๋ ์ ๋ ฅํ์ง ์์์ ๋
// ์๋ 3๊ฐ ๋์ผ
addEventListener('click', handler); // 3๋ฒ์งธ ์ธ์์ ์๋ฌด๊ฒ๋ ์
๋ ฅํ์ง ์์ผ๋ฉด ๋ฒ๋ธ๋ง ๊ธฐ๋ณธ
addEventListener('click', handler, false);
addEventListener('click', handler, { capture: false });
์ด๋ฒคํธ ์บก์ฒ๋ง : `{ capture: true }` ํน์ `true`
// ์๋ 2๊ฐ ๋์ผ
addEventListener('click', handler, true);
addEventListener('click', handler, { capture: true });
๋ฆฌ์ค๋ ์ต์
addEventListener
์ 3๋ฒ์งธ ์ธ์์ capture์ธ์ 2๊ฐ์ง ์ต์
์ด ๋ ์๋ค.
โถ once : ๊ฐ Boolean. `true`๋ฉด ์ด๋ฒคํธ๋ 1๋ฒ๋ง ์คํ๋๊ณ ๋ฆฌ์ค๋๊ฐ ์ญ์ ๋๋ค
addEventListener('click', handler, { once: true }); // ํน์ { once: false }
โท passive : ๊ฐ Boolean. `true` ๋ฉด ์ด๋ฒคํธ ํธ๋ค๋ฌ์ `preventDefault()` ๊ฐ ์์ด๋ ํธ์ถํ์ง ์๋๋ค
`preventDefault` API ํธ์ถ ์ฌ๋ถ๋ฅผ ๋ช ์ํ๋ ์ต์ ์ด๋ค. ๋ธ๋ผ์ฐ์ ๋ ๊ธฐ๋ณธ์ ์ผ๋ก `preventDefault` ํธ์ถ ์ฌ๋ถ๋ฅผ ๊ฐ์ํ๋๋ฐ, `passive` ์์ฑ์ `true` ๋ก ์ค์ผ๋ก์จ ์ด๋ฐ ๊ฐ์ ๋น์ฉ์ ์ค์ผ ์ ์๋ค.
addEventListener('click', handler, { passive: true }); // ํน์ { passive: false }
๐ก ๋ช ์ํ์ง ์์์ ๋ ๊ธฐ๋ณธ๊ฐ์ `false` ์ง๋ง wheel, mousewheel, touchstart, touchmove ์ด๋ฒคํธ์์ ๊ธฐ๋ณธ๊ฐ์ด `true` ์ธ ์ ์ฃผ์(Safari, IE ์ ์ธ) — ์ฐธ๊ณ
๋ฆฌ์ค๋ ์ ๊ฑฐ removeEventListener
์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ์ ๊ฑฐํ ๋ removeEventListener
๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ค. ์ฃผ์ํ ์ ์ ์ด๋ฒคํธ๋ฅผ ๋ฑ๋กํ์ ๋์ ํธ๋ค๋ฌ(์ฝ๋ฐฑ)์, capture
๋ฆฌ์ค๋ ์ต์
(๋ฆฌ์ค๋ 3๋ฒ์งธ ๋งค๊ฐ๋ณ์) ๊ฐ์ด ์ผ์นํด์ผ๋ง ์ ๊ฑฐ๋๋ค. ๋ฐ๋ผ์ ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ์ ๊ฑฐํ ๋ ์ด๋ฒคํธ๋ฅผ ๋ฑ๋กํ์ ๋์ ๋์ผํ ํธ๋ค๋ฌ์ capture
์ต์
์ ๋ช
์ํด์ผ ํ๋ค.
๐ก `once` ๋ `passive` ์ต์ ์ ๋ฆฌ์ค๋๋ฅผ ์ ๊ฑฐํ ๋ ๊ฒ์ฌํ์ง ์์ผ๋ฏ๋ก ์ผ์นํ์ง ์์๋ ๋๋ค.
// ์ด๋ฒคํธ ๋ฑ๋ก(3๋ฒ์งธ ์ธ์์ ์๋ฌด๊ฒ๋ ๋ช
์ํ์ง ์์์ผ๋ฏ๋ก { capture: false }์ ๊ฐ๋ค)
element.addEventListener('click', handler);
// ์ด๋ฒคํธ ์ ๊ฑฐ
element.removeEventListener('click', handler); // ์ ๊ฑฐ ์ฑ๊ณต
element.removeEventListener('click', handler, false); // ์ ๊ฑฐ ์ฑ๊ณต
element.removeEventListener('click', handler, { capture: false }); // ์ ๊ฑฐ ์ฑ๊ณต
element.removeEventListener('click', handler, { passive: true }); // ์ ๊ฑฐ ์ฑ๊ณต
element.removeEventListener('click', handler, true); // ์ ๊ฑฐ ์คํจ(capture ์ต์
์ด ๋ค๋ฅด๋ฏ๋ก)
element.removeEventListener('click', handler2, false); // ์ ๊ฑฐ ์คํจ(ํธ๋ค๋ฌ๊ฐ ๋ค๋ฅด๋ฏ๋ก)
์ด๋ฒคํธ ๋จ๊ณ | Event Phase
<div>
DIV
<p>
P<span>SPAN</span>
</p>
</div>;
for (let elem of document.querySelectorAll('*')) {
// ์ฒซ๋ฒ์งธ ๋ฆฌ์ค๋
elem.addEventListener('click', (e) => alert(`์บก์ณ๋ง: ${elem.tagName}`), true);
// ๋๋ฒ์งธ ๋ฆฌ์ค๋
elem.addEventListener('click', (e) => alert(`๋ฒ๋ธ๋ง: ${elem.tagName}`));
}
์ ์ฝ๋์ ์ด๋ฒคํธ ํ๋ฆ์ ์๋ 3๊ฐ์ง ๋จ๊ณ๋ฅผ ๊ฑฐ์น๋ค.
- Capturing ๋จ๊ณ : ์ด๋ฒคํธ๊ฐ ํ์ ์์๋ก ์ ํ๋๋ ๋จ๊ณ
- Target ๋จ๊ณ : ์ด๋ฒคํธ๊ฐ (์ด๋ฒคํธ๊ฐ ๋ฐ์ํ)Target ์์์ ์ ๋ฌ๋๋ ๋จ๊ณ
- Bubbling ๋จ๊ณ : ์ด๋ฒคํธ๊ฐ ์์ ์์๋ก ์ ํ๋๋ ๋จ๊ณ
HTML
ํ์์ ์๋ ๋ชจ๋ ํ๊ทธ์ ์ด๋ฒคํธ ์บก์ฒ๋ง / ๋ฒ๋ธ๋ง ์์๋ก ํธ๋ค๋ฌ๋ฅผ ํ ๋นํ๊ณ ๊ฐ์ฅ ์๋์ ์๋ span
ํ๊ทธ๋ฅผ ํด๋ฆญํด๋ณด๋ฉด ์๋ ์์๋๋ก ์ด๋ฒคํธ๊ฐ ์ ๋ฌ๋๋๊ฑธ ํ์ธํ ์ ์๋ค.
HTML
→BODY
→DIV
→P
(์ด๋ฒคํธ ์บก์ฒ๋ง ๋จ๊ณ / 1๋ฒ์งธ ๋ฆฌ์ค๋)SPAN
(Target ๋จ๊ณ / ์บก์ฒ๋ง, ๋ฒ๋ธ๋ง ๋ฆฌ์ค๋ ๋ ๋ค ์ค์ ํ์ผ๋ฏ๋ก 2๋ฒ ํธ์ถ)P
→DIV
→BODY
→HTML
(์ด๋ฒคํธ ๋ฒ๋ธ๋ง ๋จ๊ณ / 2๋ฒ์งธ ๋ฆฌ์ค๋)
๐ก `event.eventPhase` ๋ฉ์๋๋ก ์ด๋ฒคํธ ์คํ ๋จ๊ณ๋ฅผ ํ์ธํ ์๋ ์๋ค. ๋ฐํํ๋ ๊ฐ์ `0` ์ด๋ฒคํธ ์์, `1` ์ด๋ฒคํธ ์บก์ฒ, `2` ์ด๋ฒคํธ ํ๊ฒ, `3` ์ด๋ฒคํธ ๋ฒ๋ธ
See the Pen Event Phase ์ด๋ฒคํธ ํ๋ฆ by ColorFilter (@colorfilter) on CodePen.
e.target / e.currentTarget
event.currentTarget
: ํ์ฌ ์คํ์ค์ธ ํธ๋ค๋ฌ๊ฐ ํ ๋น๋์ด ์๋ ์์(์ด๋ฒคํธ๋ฅผ ํธ๋ค๋งํ๊ณ ์๋ ํ์ฌ ์์)event.target
: ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ ์์. ์ด๋ฒคํธ ์ ํ๊ฐ ์งํ๋๋๋ผ๋ ๋ณํ์ง ์๋๋ค
์๋ ์ฝ๋์์ <p>
ํ๊ทธ์ ๋ถ๋ชจ ์์์ธ <div>
์ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ํ ๋นํ๋ค. p
ํ๊ทธ๋ฅผ ํด๋ฆญํ๋ฉด, ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ ๊ณณ(p ํ๊ทธ)์ด๋ฏ๋ก target
์ด ๋๊ณ , ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ๋ฑ๋กํด๋์ div
๋ currentTarget
์ด ๋๋ค.
<p>
ํ๊ทธ๋ ์ด๋ฒคํธ ๋ฒ๋ธ๋ง์ผ๋ก ์ด๋ฒคํธ๋ฅผ ์์๋ฐ์๊ธฐ ๋๋ฌธ์, <div>
ํ๊ทธ์ addEventListener
๋ก <p>
ํ๊ทธ ์ ๋ณด๊ฐ ์ ๋ฌ๋ ๊ฒ์ด๋ค.
<div>
DIV ํ๊ทธ
<p>P ํ๊ทธ</p>
</div>;
const $div = document.querySelector('div');
$div.addEventListener('click', (e) => {
console.log(`currentTarget: ${e.currentTarget.tagName}`);
console.log(`target: ${e.target.tagName}`);
});
// <p> ํ๊ทธ ํด๋ฆญ์...
// "currentTarget: DIV"
// "target: P"
See the Pen event.currentTarget vs event.target by ColorFilter (@colorfilter) on CodePen.
์ด๋ฒคํธ ์ ํ ์ค๋จ Stop Propagation
์๋ ์ ํ์ค๋จ API๋ฅผ ์ฌ์ฉํ๋ฉด, โ์ด๋ฒคํธ ๋ฒ๋ธ๋ง์ผ ๋ ํด๋ฆญํ ์์์ ์ด๋ฒคํธ๋ง ๋ฐ์์ํค๊ณ ์์ ์์๋ก ์ด๋ฒคํธ๋ฅผ ์ ๋ฌํ์ง ์๋๋ค. โ์ด๋ฒคํธ ์บก์ฒ๋ง์ผ ๋ ํด๋ฆญํ ์์์ ์ต์์ ์์์ ์ด๋ฒคํธ๋ง ๋์์ํค๊ณ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ ์์๋ฅผ ์ ์ธํ ๋ค๋ฅธ ํ์ ์์์ ์ด๋ฒคํธ๋ฅผ ์ ๋ฌํ์ง ์๋๋ค.
const eventHandler = (event) => {
// ์๋ต...
event.stopPropagation();
event.preventDefault();
event.stopImmediatePropagation();
}
event.preventDefault()
: ์ด๋ฒคํธ ๊ธฐ๋ณธ ๋์ ์ค๋จevent.stopPropagation()
: ์ด๋ฒคํธ ์ ํ ์ค๋จevent.stopImmediatePropagation()
: ์ด๋ฒคํธ ์ ํ ์ค๋จ. ํ์ฌ ๋ ๋ฒจ์ ๊ฑธ๋ ค ์๋ ๋ค๋ฅธ ์ด๋ฒคํธ๋ ์ค๋จ.
์ด๋ฒคํธ ์ ํ ์ค๋จ API๋ฅผ ์ฌ์ฉํ ์์ญ์ Dead Area๊ฐ ๋๋ฏ๋ก click ๊ฐ์ ์ฌ์ฉ์ ์ด๋ฒคํธ๋ฅผ ๋ถ์ํ ๋ Dead Area๋ ๋ถ์ ํ ์ ์๊ฒ ๋๋ค. ์ํคํ ์ฒ๋ฅผ ์ ๊ณ ๋ คํ ๊ฒฝ์ฐ๊ฐ ์๋๋ผ๋ฉด ์ด๋ฒคํธ ์ ํ ์ค๋จ API๋ฅผ ๋จ์ฉํ์ง ์๋๋ก ํ๋ค.
์ด๋ฒคํธ ๋ฒ๋ธ๋ง์ ๋ง์์ผ ํ๋ ๊ฒฝ์ฐ๋ ๊ฑฐ์ ์์ต๋๋ค. ๋ฒ๋ธ๋ง์ ๋ง์์ผ ํด๊ฒฐ๋๋ ๋ฌธ์ ๋ผ๋ฉด ์ปค์คํ ์ด๋ฒคํธ ๋ฑ์ ์ฌ์ฉํด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์์ต๋๋ค... ํธ๋ค๋ฌ์ event ๊ฐ์ฒด์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํด ๋ค๋ฅธ ํธ๋ค๋ฌ์์ ์ฝ์ ์ ์๊ฒ ํ๋ฉด, ์๋์ชฝ์์ ๋ฌด์จ ์ผ์ด ์ผ์ด๋๋์ง๋ฅผ ๋ถ๋ชจ ์์์ ํธ๋ค๋ฌ์๊ฒ ์ ๋ฌํ ์ ์์ผ๋ฏ๋ก, ์ด ๋ฐฉ๋ฒ์ผ๋ก๋ ์ด๋ฒคํธ ๋ฒ๋ธ๋ง์ ํต์ ํ ์ ์์ต๋๋ค. — JavaScript Info
stopPropagation() vs stopImmediatePropagation()
๐ก 1๊ฐ ์์์ ํธ๋ค๋ฌ๋ฅผ ์ฌ๋ฌ๊ฐ ํ ๋นํ๋ค๋ฉด ํธ๋ค๋ฌ๋ฅผ ํ ๋นํ ์์(์ → ์๋)๋๋ก ์๋ํ๋ค.
์๋์ฒ๋ผ 1๊ฐ ์์์ 2๊ฐ์ ํด๋ฆญ ์ด๋ฒคํธ๊ฐ ๊ฑธ๋ ค ์๊ณ , ์ด๋ฒคํธ ํธ๋ค๋ฌ์ stopPropagation()
์ ๋ช
์ํ๋ค๊ณ ๊ฐ์ ํด๋ณธ๋ค. span
์๋ฆฌ๋จผํธ๋ฅผ ํด๋ฆญํ๋ฉด ์ด๋ฒคํธ ์ ํ๋ ์ค๋จ๋์ง๋ง ํด๋ฆญํ ์๋ฆฌ๋จผํธ์ ๊ฑธ๋ ค ์๋ ํธ๋ค๋ฌ ๋ชจ๋ ์คํ๋๋ค(ํด๋น ์๋ฆฌ๋จผํธ์ ๊ฑธ๋ ค ์๋ ๋ชจ๋ ์ด๋ฒคํธ ํธ์ถ).
const $span = document.querySelector('#span');
$span.addEventListener('click', (e) => eventHandler); // 1๋ฒ์งธ ์ด๋ฒคํธ
$span.addEventListener('click', (e) => eventHandler); // 2๋ฒ์งธ ์ด๋ฒคํธ
stopImmediatePropagation()
์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ ์๋ฆฌ๋จผํธ์ ๊ฑธ๋ ค ์๋ ๋ค๋ฅธ ์ด๋ฒคํธ์ ์ ํ๋ ์ค๋จ์ํจ๋ค. ์ ์ฝ๋๋ฅผ ์๋ก๋ค๋ฉด span
์๋ฆฌ๋จผํธ๋ฅผ ํด๋ฆญํ ์๊ฐ ์ด๋ฒคํธ ์ ํ๊ฐ ์ค๋จ๋๋ฉด์ 1๋ฒ์งธ ์ด๋ฒคํธ(ํธ๋ค๋ฌ)๋ง ํธ์ถ๋๋ค. 2๋ฒ์งธ ์ด๋ฒคํธ(ํธ๋ค๋ฌ)๋ ํธ์ถ๋์ง ์๋๋ค.
See the Pen [Event] stopPropagation() vs stopImmediatePropagation() by ColorFilter (@colorfilter) on CodePen.
preventDefault()
๐ก ํฌ์ปค์ค๊ฐ ํด์ ๋๋ `blur` ์ด๋ฒคํธ๋ฅผ ๋ฐฉ์งํ ๋๋ `preventDefault` ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ค — ์ฐธ๊ณ ๋ ธํธ
<a id="a" href="https://romantech.net" target="_blank">
A ์์ญ ๋งํฌ
</a>;
<a>
ํ๊ทธ๋ href
์์ฑ์ ๋ช
์ํ URL๋ก ์ด๋ํ๋ ์ด๋ฒคํธ ๋์์ ๊ธฐ๋ณธ์ ์ผ๋ก ๊ฐ์ง๊ณ ์๋ค. <a>
ํ๊ทธ ์ฒ๋ผ ๊ธฐ๋ณธ์ ์ผ๋ก ๊ฐ์ง๊ณ ์๋ (์ด๋ฒคํธ)๋์์ ๋ฐฉ์งํ๊ณ ์ถ์ ๋ ํธ๋ค๋ฌ์ preventDefault()
๋ฅผ ๋ช
์ํ๋ฉด ๋๋ค. ๊ทธ๋ผ <a>
ํ๊ทธ๋ ํด๋ฆญํด๋ ๋งํฌ๋ก ์ด๋ํ์ง ์๊ณ , <form>
ํ๊ทธ๋ submit ๋ฒํผ์ ๋๋ฌ๋ ์๋ฒ๋ก ์๋ ์ ์ก๋์ง ์๋๋ค.
See the Pen [Event] preventDefault() by ColorFilter (@colorfilter) on CodePen.
์ด๋ฒคํธ ์์ | Event Delegation
See the Pen Event Delegation ์ด๋ฒคํธ ์์ by ColorFilter (@colorfilter) on CodePen.
์ด๋ฒคํธ ์์์ ํ์ ์์์ ์ด๋ฒคํธ๋ฅผ ๋ถ์ด์ง ์๊ณ , ์์ ์์์์ ํ์ ์์์ ์ด๋ฒคํธ๋ฅผ ์ ์ดํ๋ ๋ฐฉ์(ํจํด)์ด๋ค. ์๋ ์์ ์์ ํ ์ผ ๋ชฉ๋ก์ ์๋ ์ฒดํฌ๋ฐ์ค์ ์ฒดํฌํ๋ฉด, ์ฒดํฌํ ํ ์ผ ํ
์คํธ๊ฐ ์ฐ์ธก DoneList
์ ์ถ๊ฐ๋๋ค. ์ฒดํฌ๋ฐ์ค <input type="checkbox">
์ ์ผ์ผ์ด ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ๋ถ์ผ ์ ์์ง๋ง, ๊ทธ๋ ๊ฒ ํ๋ฉด ์๋ก์ด ํ ์ผ์ ์ถ๊ฐํ ๋๋ง๋ค ์ด๋ฒคํธ๋ฅผ ๋ถ์ฌ์ผ ํ๊ธฐ ๋๋ฌธ์ ๋ฒ๊ฑฐ๋กญ๋ค.
์ด๋ฐ ๋ฒ๊ฑฐ๋ก์์ ํด์ํ๊ธฐ ์ํด ์ฌ์ฉํ๋ ๊ฒ์ด ๋ฐ๋ก ์ด๋ฒคํธ ์์ ํจํด์ด๋ค. ๊ฐ์ฅ ์์์ ์๋ <ul>
ํ๊ทธ์๋ง ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ์ถ๊ฐํด๋๋ฉด, ์ด๋ฒคํธ ๋ฒ๋ธ๋ง์ผ๋ก ์ธํด ํ์ ์์์์ ๋ฐ์ํ ์ด๋ฒคํธ๋ฅผ ๊ฐ์งํ๋ค. ์๋ก์ด ํ ์ผ(์ฒดํฌ๋ฐ์ค)์ด ์ถ๊ฐ๋ผ๋ ์ด๋ฒคํธ๋ฅผ ์๋์ผ๋ก ๋ถ์ด์ง ์์๋ ๋๊ธฐ ๋๋ฌธ์ ํธ๋ฆฌํ๋ค.
๐ก radio ํ์ ์ `<input>` ์ 1๊ฐ๋ง ์ ํ ํ ์ ์๊ณ , checkbox ํ์ ์ `<input>` ์ ์ฌ๋ฌ ๊ฐ ์ ํ ํ ์ ์๋ค. `name` ์์ฑ์ ์ฒดํฌ๋ฐ์ค์ ์ด๋ฆ์ ๋ํ๋ด๋ฉฐ ๊ฐ์ ๋ถ๋ฅ์ ์ฒดํฌ๋ฐ์ค๋ฅผ ๊ทธ๋ฃน์ผ๋ก ๋ฌถ์ ๋ ์ฌ์ฉํ๋ค. React, Vue์์ ํน์ `<input>` ํ๊ทธ๋ฅผ ์๋ณํ ๋ `name` ์์ฑ์ ํ์ฉํ๊ธฐ๋ ํ๋ค. form ํ๊ทธ๋ก ๋ฐ์ดํฐ๋ฅผ ์ ์กํ๋ฉด `<input>` ์ `name`, `value` ์์ฑ ๊ฐ์ด `...?name=value` ํํ๋ก ์ ์ก๋๋ค. — ์ฐธ๊ณ ๋งํฌ
๐ก `<label>` ํ๊ทธ์ `<input>` ํ๊ทธ๋ฅผ ์ฐ๊ฒฐํ๋ฉด `<label>` ์ textContent ์์ญ๋ง ํด๋ฆญํด๋ input ์ฒดํฌ๋ฐ์ค๋ฅผ ํธ๋ค๋งํ ์ ์๋ค. `<label>` ํ๊ทธ์ `for` ์์ฑ๊ณผ `<input>` ํ๊ทธ์ `id` ์์ฑ ๊ฐ์ ๋์ผํ๊ฒ ์ ๋ ฅํ๋ฉด ๋ ํ๊ทธ๋ฅผ ์ฐ๊ฒฐํ ์ ์๋ค. ์ ๊ทผ์ฑ์ ๊ณ ๋ คํด `<input>` ๊ณผ `<label>` ํ๊ทธ๋ ๊ฐ์ด ์ฐ๋๊ฒ ์ข๋ค. `<input>` ํ๊ทธ๊ฐ `<label>` ์์ชฝ์ ์๋ค๋ฉด ์ฐ๊ฒฐ๋ ์ํ๊ฐ ๋๋ฏ๋ก `for`, `id` ๋ฅผ ์ ๋ ฅํ์ง ์์๋ ๋๋ค. — ์ฐธ๊ณ ๋งํฌ
<ul class="itemList">
<li>
<input type="checkbox" id="item1" />
<label for="item1">Learn JavaScript</label>
</li>
</ul>;
const $itemList = document.querySelector('.itemList');
$itemList.addEventListener('input', updateDoneList);
// updateDoneList ํธ๋ค๋ฌ๋ ์ฒดํฌํ ํ ์ผ์ DoneList์ ์ถ๊ฐํ๋ค.
// input ์ด๋ฒคํธ ํ์
์ <input>, <select>, <textarea> ์์์ value ์์ฑ์ด ๋ฐ๋ ๋๋ง๋ค ํธ์ถ
elem.closest()
addEventListener์ ์ด๋ฒคํธ ํ์
์ 'input'
์ผ๋ก ์
๋ ฅํ๊ธฐ ๋๋ฌธ์ <input>
ํ๊ทธ์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ์ ๋๋ง ํธ์ถ๋๋ค. 'input'
์ด๋ฒคํธ ํ์
์ <input>
<select>
<textarea>
์์์ value ์์ฑ์ด ๋ฐ๋ ๋๋ง ํธ์ถ๋๊ธฐ ๋๋ฌธ์ด๋ค.
๐ก elem.closest(selector)
๋ฉ์๋๋ก ๋ถ๋ชจ ์์๋ฅผ ์ฝ๊ฒ ํ์ํ ์ ์๋ค. closest()
ํ๋ผ๋ฏธํฐ์ ๋ช
์ํ ๋ฌธ์์ด๊ณผ ์ผ์นํ๋ ๋
ธ๋๋ฅผ ์ฐพ์ ๋๊น์ง ์์ (e.target)๋ถํฐ ์์ํด์ ๋ถ๋ชจ ์์ ๋จ์๋ก ํ์ํ๋ค(๋ฌธ์ ๋ฃจํธ๊น์ง). ์กฐ๊ฑด์ ๋ง์กฑํ ๊ฐ์ฅ ๊ฐ๊น์ด ์์๋ฅผ ๋ฐํํ๋ฉฐ(๋ถ๋ชจ ์์๋ฅผ ์ฐพ์ ๋ ๊ฐ์ฅ ๊ฐ๊น์ด ๋ถ๋ชจ), ์์ผ๋ฉด null
์ ๋ฐํํ๋ค. ์์ ๋ถํฐ ํ์ํ๋ฏ๋ก ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ ์๋ฆฌ๋จผํธ ์์ ์ ๋ฐํํ ์๋ ์๋ค.
๋ง์ฝ ์ด๋ฒคํธ ํ์
์ click
์ผ๋ก ์
๋ ฅํ๋ค๋ฉด <input>
๋ฟ๋ง ์๋๋ผ, <label>
<li>
ํ๊ทธ์ ์ด๋ฒคํธ๋ ๊ฐ์งํ๋ค. ์ด ์ํฉ์์ <input>
ํ์
์ด๋ฒคํธ๋ง ์ฌ์ฉํ๋ ค๋ฉด elem.closest()
์ ์ฌ์ฉํ๋ฉด ๋๋ค.
// addToDoneList ์ด๋ฒคํธ ํธ๋ค๋ฌ
const updateDoneList = (e) => {
// ๋์ด์จ ์ด๋ฒคํธ๊ฐ checkbox ํ์
์ input ์์๋ผ๋ฉด, ํด๋น ์์ ์ ๋ณด๊ฐ input ๋ณ์์ ๋ด๊ธด๋ค
// ์
๋ ํฐ์ ๋ช
์ํ ๋
ธ๋๋ฅผ ์ฐพ์ง ๋ชปํ๋ค๋ฉด null ๋ฐํ
const input = e.target.closest('input[type=checkbox]');
if (input) {
// ...
}
};
๋ฐ์ดํฐ ์์ฑ | Data Attribute
์ด๋ ์๋ฆฌ๋จผํธ์๋ data-
๋ก ์์ํ๋ ์์ฑ์ ์ฌ์ฉํ ์ ์๋ค. ํ๋ฉด์ ๋ณด์ด์ง ์๋ ํ
์คํธ๋ ์ ๋ณด๋ฅผ ์๋ฆฌ๋จผํธ์ ๋ด์๋ ๋ ์ ์ฉํ๋ค. React๋ Vue์์ ๋ฆฌ์คํธ ์๋ฆฌ๋จผํธ๋ฅผ ๋ ๋ํ ๋ key
๋ฅผ ์ง์ ํ๋๋ฐ, ์ด๊ฒ ์ญ์ ๋ฐ์ดํฐ ์์ฑ์ ์ฌ์ฉํด์ ๊ตฌํํ ์ ์๋ค.
<li data-key="item3">Do exercise</li>;
์๋ฐ์คํฌ๋ฆฝํธ์์ elem.dataset
๊ฐ์ฒด๋ก ์ ๊ทผํ ์ ์๋ค.
e.target.dataset.key; // item3
document.querySelect('li').dataset.key; // item3
๋ฐ์ดํฐ ์์ฑ์ HTML ์์ฑ์ด๊ธฐ ๋๋ฌธ์ CSS๋ฅผ ํตํด ์ ๊ทผํด์ ํ๋ฉด์ ๋ณด์ด๋๋ก ํ ์๋ ์๋ค.
li::before {
content: attr(data-key);
}
๊ฐ ๋ฐ์ดํฐ ๊ฐ์ ๋ฐ๋ผ ์คํ์ผ์ ๋ค๋ฅด๊ฒ ์ง์ ํ ์๋ ์๋ค.
li[data-key='item3'] {
color: red;
}
๋ฐ์ดํฐ ์์ฑ๊ณผ ์ด๋ฒคํธ ์์ ํ์ฉ
์ ์ฅํ๊ธฐ, ๋ถ๋ฌ์ค๊ธฐ ๋ฑ์ ๋ฒํผ์ ๊ตฌํํด์ผ ๋๋ค๊ณ ๊ฐ์ ํด๋ณธ๋ค. ๊ฐ ๋ฒํผ ์ก์ ์ ๋ฉ์๋๋ ์ด๋ฏธ ๊ตฌํ๋ ์ํ๋ค. ๋ค์ ์คํ ์ ๋ฒํผ๊ณผ ๋ฉ์๋๋ฅผ ์ฐ๊ฒฐํ ์ฐจ๋ก. ์ด๋ ๊ฐ ๋ฒํผ์ ์ผ์ผ์ด ํธ๋ค๋ฌ๋ฅผ ํ ๋นํ์ง ์๊ณ ์ด๋ฒคํธ ์์๊ณผ ๋ฐ์ดํฐ ์์ฑ์ ์ฌ์ฉํด์ ๋์ฑ ์ธ๋ จ๋๊ฒ ๊ตฌํํ ์ ์๋ค.
<div id="menu">
<button data-action="save">์ ์ฅํ๊ธฐ</button>
<button data-action="load">๋ถ๋ฌ์ค๊ธฐ</button>
<button data-action="search">๊ฒ์ํ๊ธฐ</button>
</div>;
const buttonAction = {
save() {
alert('์ ์ฅํ๊ธฐ');
},
load() {
alert('๋ถ๋ฌ์ค๊ธฐ');
},
search() {
alert('๊ฒ์ํ๊ธฐ');
},
};
// id ์์ฑ์ querySelector๋ฅผ ์ฐ์ง ์์๋ id ์ด๋ฆ์ผ๋ก ์๋ฆฌ๋จผํธ์ ์ ๊ทผํ ์ ์๋ค.
menu.addEventListener('click', (e) => {
const actionName = e.target.dataset.action; // save ํน์ load ํน์ search
if (actionName) {
buttonAction[actionName]();
}
});
See the Pen ๋ฐ์ดํฐ ์์ฑ๊ณผ ์ด๋ฒคํธ ์์ ํ์ฉ ์์ 1 by ColorFilter (@colorfilter) on CodePen.
ํ๋ ํจํด — ์นด์ดํฐ ๊ตฌํ
๐ก `document` ๋ ๋ฒจ์ ํธ๋ค๋ฌ๋ฅผ ํ ๋นํ ๋ ํญ์ `addEventListener`๋ฅผ ์ฌ์ฉํ๋ค. `document`์ ์ฌ๋ฌ๊ฐ์ ํธ๋ค๋ฌ๋ฅผ ํ ๋นํ๋ ๊ฒ์ ํ๋ก์ ํธ์์ ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉ๋๋ ๋ฐฉ์์ด๋ค.
์ด๋ฒคํธ ์์๊ณผ ๋ฐ์ดํฐ ์์ฑ์ ํ์ฉํด ์ ์ธ์ ๋ฐฉ์์ผ๋ก ํน์ "ํ๋"์ ์ถ๊ฐํ ์ ์๋ค. ์๋ ์์๋ ๋ฒํผ์ ํด๋ฆญํ๋ฉด ์ซ์๊ฐ ์ฆ๊ฐํ๋ "ํ๋"์ ๋ฐ์ดํฐ ์์ฑ์ data-counter
๋ฅผ ์ด์ฉํด ๋ถ์ฌํ ์์
<input type="button" value="1" data-counter />
<input type="button" value="2" data-counter />
document.addEventListener('click', (e) => {
// ๋ฐ์ดํฐ ์์ฑ์ ๊ฐ์ ์๊ณ key(data-counter)๋ง ์๋ค๋ฉด ์กฐํํ์ ๋ ๋น ๋ฌธ์์ด๋ก ๋์จ๋ค
// ๋ฐ๋ผ์ ์๋์ฒ๋ผ !== undefined ์กฐ๊ฑด์ ๋ช
์ํ๋ค
if (e.target.dataset.counter !== undefined) {
event.target.value++;
}
});
See the Pen ๋ฐ์ดํฐ ์์ฑ๊ณผ ์ด๋ฒคํธ ์์ ํ์ฉ ์์ 2 - ์นด์ดํฐ ๊ตฌํ(ํ๋ ํจํด) by ColorFilter (@colorfilter) on CodePen.
ํ๋ ํจํด — ํ ๊ธ๋ฌ ๊ตฌํ
data-toggle-id
๋ฐ์ดํฐ ์์ฑ์ด ์๋ ์์(์๋ ์์ ์์ button)๋ฅผ ํด๋ฆญํ๋ฉด, ํด๋น ๋ฐ์ดํฐ ์์ฑ ๊ฐ์ id
๋ก ๊ฐ์ง๋ ์์๋ฅผ ๋ณด์ด๊ฑฐ๋ ์ฌ๋ผ์ง๋๋ก ํ ์ ์๋ค. ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ๋ ํ๊ทธ์ data-toggle-id
์์ฑ๋ง ์ถ๊ฐํ๋ฉด(ํ ๊ธ ํ๋์ ์ ์ธ ํด์ฃผ๊ธฐ๋ง ํ๋ฉด) ์์์ ํ ๊ธ ๊ธฐ๋ฅ์ ์ถ๊ฐํ ์ ์๊ธฐ ๋๋ฌธ์ ํธ๋ฆฌํ๋ค.
๐ก html ํ๊ทธ์ hidden
์์ฑ์ ์ถ๊ฐํ๋ฉด ํด๋น ์์๋ฅผ ํ๋ฉด์์ ๊ฐ์ถ ์ ์๋ค. ํ๋ฉด์ ๋ณด์ด์ง ์์ง๋ง ์์ญ์ ๊ทธ๋๋ก ์ ์งํ๋ visibility: hidden
๊ณผ๋ ๋ฌ๋ฆฌ, ๊ฐ์ถ ์์ญ๋ ์ฌ๋ผ์ง๋ค. display: none;
์์ฑ๊ณผ ๋น์ทํ๋ค(CSS). ์คํฌ๋ฆฐ ๋ฆฌ๋ ๊ฐ์ ๋ค๋ฅธ ํ์ ๋ฐฉ์์๋ ์จ๊ฒจ์ง๋ฏ๋ก ์ฃผ์ํ๋ค.
<button data-toggle-id="subscribe-mail">๊ตฌ๋
ํผ ๋ณด๊ธฐ</button>
<section id="subscribe-mail" hidden>
<form>์ด๋ฉ์ผ<input type="email" /></form>
</section>
document.addEventListener('click', (e) => {
const id = e.target.dataset.toggleId;
if (!id) return;
const elem = document.getElementById(id);
elem.hidden = !elem.hidden;
});
See the Pen ๋ฐ์ดํฐ ์์ฑ๊ณผ ์ด๋ฒคํธ ์์ ํ์ฉ ์์ 3 - ํ ๊ธ๋ฌ ๊ตฌํ(ํ๋ ํจํด) by ColorFilter (@colorfilter) on CodePen.
๋ ํผ๋ฐ์ค
์ ์ฉํ ์ฌ์ดํธ
์ฐธ๊ณ ์ฌ์ดํธ
- ๋ฒ๋ธ๋ง๊ณผ ์บก์ฒ๋ง
- ์ด๋ฒคํธ ๋ฒ๋ธ๋ง, ์ด๋ฒคํธ ์บก์ฒ ๊ทธ๋ฆฌ๊ณ ์ด๋ฒคํธ ์์๊น์ง
- [JavaScript] JavaScript์์ ์ด๋ฒคํธ ์ ํ๋ฅผ ์ค๋จํ๋ ๋ค๊ฐ์ง ๋ฐฉ๋ฒ
- stopPropagation vs stopImmediatePropagation ์ ๋๋ก ์ดํดํ๊ธฐ
- JavaScript ์ด๋ฒคํธ ์ฒ๋ฆฌ
๊ธ ์์ ์ฌํญ์ ๋ ธ์ ํ์ด์ง์ ๊ฐ์ฅ ๋น ๋ฅด๊ฒ ๋ฐ์๋ฉ๋๋ค. ๋งํฌ๋ฅผ ์ฐธ๊ณ ํด ์ฃผ์ธ์
'๐ช Programming' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Git] SSH ๊ณต๊ฐํค ๋ง๋ค๊ธฐ / Credential ์ธ์ฆ ์์คํ (0) | 2024.04.26 |
---|---|
[HTML/CSS] focus-within โ ์์ ์์๊ฐ ํฌ์ปค์ค ๋์ ๋ ๋ถ๋ชจ ์คํ์ผ ์ง์ (0) | 2024.04.25 |
[HTML/CSS] ์๋งจํฑ ํ๊ทธ Semantic Tag (0) | 2024.04.25 |
[JS] ์๋ฉด ์ ์ฉํ ์๋ฐ์คํฌ๋ฆฝํธ ์ต์ ๋ฌธ๋ฒ (0) | 2024.04.25 |
[JS] ์๋ฐ์คํฌ๋ฆฝํธ ์ต์ ๋ ์ฒด์ด๋ (0) | 2024.04.25 |
๋๊ธ
์ด ๊ธ ๊ณต์ ํ๊ธฐ
-
๊ตฌ๋
ํ๊ธฐ
๊ตฌ๋ ํ๊ธฐ
-
์นด์นด์คํก
์นด์นด์คํก
-
๋ผ์ธ
๋ผ์ธ
-
ํธ์ํฐ
ํธ์ํฐ
-
Facebook
Facebook
-
์นด์นด์ค์คํ ๋ฆฌ
์นด์นด์ค์คํ ๋ฆฌ
-
๋ฐด๋
๋ฐด๋
-
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
-
Pocket
Pocket
-
Evernote
Evernote
๋ค๋ฅธ ๊ธ
-
[Git] SSH ๊ณต๊ฐํค ๋ง๋ค๊ธฐ / Credential ์ธ์ฆ ์์คํ
[Git] SSH ๊ณต๊ฐํค ๋ง๋ค๊ธฐ / Credential ์ธ์ฆ ์์คํ
2024.04.26 -
[HTML/CSS] focus-within — ์์ ์์๊ฐ ํฌ์ปค์ค ๋์ ๋ ๋ถ๋ชจ ์คํ์ผ ์ง์
[HTML/CSS] focus-within — ์์ ์์๊ฐ ํฌ์ปค์ค ๋์ ๋ ๋ถ๋ชจ ์คํ์ผ ์ง์
2024.04.25 -
[HTML/CSS] ์๋งจํฑ ํ๊ทธ Semantic Tag
[HTML/CSS] ์๋งจํฑ ํ๊ทธ Semantic Tag
2024.04.25 -
[JS] ์๋ฉด ์ ์ฉํ ์๋ฐ์คํฌ๋ฆฝํธ ์ต์ ๋ฌธ๋ฒ
[JS] ์๋ฉด ์ ์ฉํ ์๋ฐ์คํฌ๋ฆฝํธ ์ต์ ๋ฌธ๋ฒ
2024.04.25