JavaScript ์ด๋ฒคํธ์ DOM
JavaScript ์ด๋ฒคํธ์ DOM¶
๊ฐ์¶
DOM(Document Object Model)์ HTML ๋ฌธ์๋ฅผ JavaScript๋ก ์กฐ์ํ ์ ์๊ฒ ํด์ฃผ๋ ์ธํฐํ์ด์ค์ ๋๋ค. ์ด๋ฒคํธ๋ ์ฌ์ฉ์ ์ํธ์์ฉ(ํด๋ฆญ, ์ ๋ ฅ ๋ฑ)์ ์ฒ๋ฆฌํ๋ ๋ฉ์ปค๋์ฆ์ ๋๋ค.
์ ์ ์ง์: 06_JS_Basics.md
๋ชฉ์ฐจ¶
- DOM ๊ธฐ์ด
- ์์ ์ ํ
- ์์ ๋ด์ฉ ์กฐ์
- ์์ฑ ์กฐ์
- ํด๋์ค ์กฐ์
- ์คํ์ผ ์กฐ์
- ์์ ์์ฑ๊ณผ ์ญ์
- ์ด๋ฒคํธ ๊ธฐ์ด
- ์ด๋ฒคํธ ์ข ๋ฅ
- ์ด๋ฒคํธ ์์
- ํผ ์ฒ๋ฆฌ
DOM ๊ธฐ์ด¶
DOM ํธ๋ฆฌ ๊ตฌ์กฐ¶
document
โโโ html
โโโ head
โ โโโ title
โโโ body
โโโ header
โ โโโ h1
โโโ main
โ โโโ p
โ โโโ div
โโโ footer
๋ ธ๋ ํ์ ¶
// ์์ ๋
ธ๋ (Element)
document.body
// ํ
์คํธ ๋
ธ๋ (Text)
document.body.firstChild
// ๋ฌธ์ ๋
ธ๋ (Document)
document
// ์ฃผ์ ๋
ธ๋ (Comment)
<!-- ์ฃผ์ -->
DOM ํ์¶
const element = document.querySelector('.box');
// ๋ถ๋ชจ/์์
element.parentNode // ๋ถ๋ชจ ๋
ธ๋
element.parentElement // ๋ถ๋ชจ ์์
element.children // ์์ ์์๋ค (HTMLCollection)
element.childNodes // ์์ ๋
ธ๋๋ค (ํ
์คํธ ํฌํจ)
element.firstChild // ์ฒซ ๋ฒ์งธ ์์ ๋
ธ๋
element.firstElementChild // ์ฒซ ๋ฒ์งธ ์์ ์์
element.lastChild // ๋ง์ง๋ง ์์ ๋
ธ๋
element.lastElementChild // ๋ง์ง๋ง ์์ ์์
// ํ์
element.nextSibling // ๋ค์ ํ์ ๋
ธ๋
element.nextElementSibling // ๋ค์ ํ์ ์์
element.previousSibling // ์ด์ ํ์ ๋
ธ๋
element.previousElementSibling // ์ด์ ํ์ ์์
parentElement
โ
โโโโโโโโโโโโโโผโโโโโโโโโโโโโ
โ โ โ
previousElement element nextElement
โ
โโโโโโโโโดโโโโโโโโ
โ โ โ
first children last
์์ ์ ํ¶
๋จ์ผ ์์ ์ ํ¶
// CSS ์ ํ์๋ก ์ฒซ ๋ฒ์งธ ์์ (๊ถ์ฅ)
document.querySelector('.class');
document.querySelector('#id');
document.querySelector('div.box');
document.querySelector('[data-id="123"]');
// ID๋ก ์ ํ
document.getElementById('myId');
๋ค์ค ์์ ์ ํ¶
// CSS ์ ํ์๋ก ๋ชจ๋ ์์ (NodeList)
document.querySelectorAll('.item');
document.querySelectorAll('ul li');
// ํด๋์ค๋ก ์ ํ (HTMLCollection - ์ค์๊ฐ)
document.getElementsByClassName('item');
// ํ๊ทธ๋ก ์ ํ (HTMLCollection - ์ค์๊ฐ)
document.getElementsByTagName('div');
// name ์์ฑ์ผ๋ก ์ ํ
document.getElementsByName('username');
NodeList vs HTMLCollection¶
// NodeList (์ ์ )
const nodeList = document.querySelectorAll('.item');
nodeList.forEach(item => console.log(item)); // forEach ์ฌ์ฉ ๊ฐ๋ฅ
// HTMLCollection (๋์ /์ค์๊ฐ)
const htmlCollection = document.getElementsByClassName('item');
// forEach ์ฌ์ฉ ๋ถ๊ฐ, ๋ฐฐ์ด๋ก ๋ณํ ํ์
[...htmlCollection].forEach(item => console.log(item));
Array.from(htmlCollection).forEach(item => console.log(item));
๋ฒ์ ๋ด ์ ํ¶
const container = document.querySelector('.container');
// container ๋ด๋ถ์์ ์ ํ
const item = container.querySelector('.item');
const items = container.querySelectorAll('.item');
closest()¶
๊ฐ์ฅ ๊ฐ๊น์ด ์กฐ์ ์์ ์ฐพ๊ธฐ
const button = document.querySelector('button');
// button์ ๊ฐ์ฅ ๊ฐ๊น์ด .card ์กฐ์
const card = button.closest('.card');
// ์๊ธฐ ์์ ๋ ํฌํจ
const self = button.closest('button'); // ์๊ธฐ ์์ ๋ฐํ
matches()¶
์ ํ์์ ์ผ์นํ๋์ง ํ์ธ
const element = document.querySelector('.item');
element.matches('.item'); // true
element.matches('.active'); // false (ํด๋์ค ์์ผ๋ฉด)
element.matches('div.item'); // true (div์ด๊ณ .item์ด๋ฉด)
์์ ๋ด์ฉ ์กฐ์¶
textContent¶
ํ ์คํธ๋ง ๋ค๋ฃน๋๋ค (HTML ํ๊ทธ ๋ฌด์).
const el = document.querySelector('.box');
// ์ฝ๊ธฐ
console.log(el.textContent);
// ์ฐ๊ธฐ (HTML ํ๊ทธ๋ ํ
์คํธ๋ก ์ฒ๋ฆฌ)
el.textContent = '<strong>๊ตต๊ฒ</strong>'; // ํ๊ทธ๊ฐ ๊ทธ๋๋ก ํ์๋จ
innerHTML¶
HTML์ ํฌํจํ ๋ด์ฉ์ ๋ค๋ฃน๋๋ค.
const el = document.querySelector('.box');
// ์ฝ๊ธฐ
console.log(el.innerHTML);
// ์ฐ๊ธฐ (HTML ํ์ฑ๋จ)
el.innerHTML = '<strong>๊ตต๊ฒ</strong>'; // ์ค์ ๋ก ๊ตต๊ฒ ํ์
// ์ถ๊ฐ
el.innerHTML += '<p>์ถ๊ฐ ๋ด์ฉ</p>';
// โ ๏ธ ๋ณด์ ์ฃผ์: ์ฌ์ฉ์ ์
๋ ฅ์ ๊ทธ๋๋ก ๋ฃ์ง ๋ง ๊ฒ!
// el.innerHTML = userInput; // XSS ์ทจ์ฝ์ !
innerText vs textContent¶
// innerText: ํ๋ฉด์ ๋ณด์ด๋ ํ
์คํธ๋ง (๋๋ฆผ)
// textContent: ๋ชจ๋ ํ
์คํธ (๋น ๋ฆ)
// display: none์ธ ์์์ ํ
์คํธ
el.innerText; // ํฌํจ ์ ๋จ
el.textContent; // ํฌํจ๋จ
outerHTML¶
์์ ์์ฒด๋ฅผ ํฌํจํ HTML
const el = document.querySelector('.box');
// ์ฝ๊ธฐ: ์์ ์์ฒด ํฌํจ
console.log(el.outerHTML); // <div class="box">๋ด์ฉ</div>
// ์ฐ๊ธฐ: ์์ ์์ฒด๋ฅผ ๊ต์ฒด
el.outerHTML = '<span>์ ์์</span>';
์์ฑ ์กฐ์¶
ํ์ค ์์ฑ¶
const link = document.querySelector('a');
const img = document.querySelector('img');
const input = document.querySelector('input');
// ์ง์ ์ ๊ทผ
link.href = 'https://example.com';
img.src = 'image.jpg';
img.alt = '์ด๋ฏธ์ง ์ค๋ช
';
input.value = '์
๋ ฅ๊ฐ';
input.disabled = true;
input.checked = true;
getAttribute / setAttribute¶
const el = document.querySelector('.box');
// ์ฝ๊ธฐ
el.getAttribute('class');
el.getAttribute('data-id');
// ์ฐ๊ธฐ
el.setAttribute('class', 'box active');
el.setAttribute('data-id', '123');
// ์ญ์
el.removeAttribute('data-id');
// ์กด์ฌ ํ์ธ
el.hasAttribute('data-id');
data ์์ฑ¶
<div id="user" data-user-id="123" data-user-name="ํ๊ธธ๋"></div>
const el = document.querySelector('#user');
// dataset์ผ๋ก ์ ๊ทผ (camelCase ๋ณํ)
el.dataset.userId // "123"
el.dataset.userName // "ํ๊ธธ๋"
// ์์
el.dataset.userId = '456';
el.dataset.newAttr = 'value'; // data-new-attr ์์ฑ
// ์ญ์
delete el.dataset.userName;
ํด๋์ค ์กฐ์¶
classList¶
const el = document.querySelector('.box');
// ์ถ๊ฐ
el.classList.add('active');
el.classList.add('highlight', 'visible'); // ์ฌ๋ฌ ๊ฐ
// ์ ๊ฑฐ
el.classList.remove('active');
el.classList.remove('highlight', 'visible');
// ํ ๊ธ (์์ผ๋ฉด ์ ๊ฑฐ, ์์ผ๋ฉด ์ถ๊ฐ)
el.classList.toggle('active');
el.classList.toggle('active', true); // ๊ฐ์ ๋ก ์ถ๊ฐ
el.classList.toggle('active', false); // ๊ฐ์ ๋ก ์ ๊ฑฐ
// ๊ต์ฒด
el.classList.replace('old-class', 'new-class');
// ํ์ธ
el.classList.contains('active'); // true/false
// ๋ชจ๋ ํด๋์ค
el.classList.length; // ํด๋์ค ๊ฐ์
el.classList.item(0); // ์ฒซ ๋ฒ์งธ ํด๋์ค
[...el.classList]; // ๋ฐฐ์ด๋ก ๋ณํ
className¶
const el = document.querySelector('.box');
// ์ ์ฒด ํด๋์ค ๋ฌธ์์ด
el.className; // "box highlight"
el.className = 'new-class'; // ์ ์ฒด ๊ต์ฒด
el.className += ' another'; // ์ถ๊ฐ (๊ณต๋ฐฑ ์ฃผ์)
์คํ์ผ ์กฐ์¶
style ์์ฑ¶
const el = document.querySelector('.box');
// ๊ฐ๋ณ ์คํ์ผ (camelCase)
el.style.backgroundColor = 'red';
el.style.fontSize = '20px';
el.style.marginTop = '10px';
el.style.display = 'none';
// CSS ์์ฑ๋ช
๊ทธ๋๋ก (๋๊ดํธ)
el.style['background-color'] = 'red';
// ์ฌ๋ฌ ์คํ์ผ ํ ๋ฒ์
el.style.cssText = 'color: red; font-size: 20px;';
// ์คํ์ผ ์ ๊ฑฐ
el.style.backgroundColor = '';
el.style.removeProperty('background-color');
getComputedStyle¶
์ค์ ์ ์ฉ๋ ์คํ์ผ ์ฝ๊ธฐ
const el = document.querySelector('.box');
const styles = getComputedStyle(el);
styles.backgroundColor; // "rgb(255, 0, 0)"
styles.fontSize; // "16px"
styles.display; // "block"
// ์์ฌ ์์ ์คํ์ผ
const beforeStyles = getComputedStyle(el, '::before');
์์ ์์ฑ๊ณผ ์ญ์ ¶
์์ ์์ฑ¶
// ์์ ์์ฑ
const div = document.createElement('div');
div.className = 'box';
div.id = 'myBox';
div.textContent = '์ ์์';
// ํ
์คํธ ๋
ธ๋ ์์ฑ
const text = document.createTextNode('ํ
์คํธ');
// ๋ฌธ์ ์กฐ๊ฐ (์ฌ๋ฌ ์์ ๋ฌถ์)
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
const item = document.createElement('li');
item.textContent = `ํญ๋ชฉ ${i}`;
fragment.appendChild(item);
}
list.appendChild(fragment); // ํ ๋ฒ๋ง DOM ์
๋ฐ์ดํธ
์์ ์ถ๊ฐ¶
const parent = document.querySelector('.parent');
const child = document.createElement('div');
// ๋์ ์ถ๊ฐ
parent.appendChild(child);
parent.append(child); // ํ
์คํธ๋ ๊ฐ๋ฅ
parent.append(child, 'ํ
์คํธ'); // ์ฌ๋ฌ ๊ฐ ๊ฐ๋ฅ
// ์์ ์ถ๊ฐ
parent.prepend(child);
// ํน์ ์์น์ ์ฝ์
const reference = document.querySelector('.reference');
parent.insertBefore(child, reference); // reference ์์
// insertAdjacentHTML
parent.insertAdjacentHTML('beforebegin', '<div>์</div>');
parent.insertAdjacentHTML('afterbegin', '<div>์ฒซ ์์</div>');
parent.insertAdjacentHTML('beforeend', '<div>๋ง์ง๋ง ์์</div>');
parent.insertAdjacentHTML('afterend', '<div>๋ค</div>');
// insertAdjacentElement
parent.insertAdjacentElement('beforeend', child);
<!-- beforebegin -->
<parent>
<!-- afterbegin -->
๊ธฐ์กด ๋ด์ฉ
<!-- beforeend -->
</parent>
<!-- afterend -->
์์ ์ญ์ ¶
const el = document.querySelector('.box');
// ์๊ธฐ ์์ ์ญ์
el.remove();
// ๋ถ๋ชจ์์ ์์ ์ญ์
parent.removeChild(el);
// ๋ชจ๋ ์์ ์ญ์
parent.innerHTML = '';
// ๋๋
while (parent.firstChild) {
parent.removeChild(parent.firstChild);
}
// ๋๋
parent.replaceChildren();
์์ ๋ณต์ ¶
const el = document.querySelector('.box');
// ์์ ๋ณต์ (์์๋ง)
const shallow = el.cloneNode(false);
// ๊น์ ๋ณต์ (์์ ํฌํจ)
const deep = el.cloneNode(true);
// ๋ฌธ์์ ์ถ๊ฐ
document.body.appendChild(deep);
์์ ๊ต์ฒด¶
const oldEl = document.querySelector('.old');
const newEl = document.createElement('div');
// ๊ต์ฒด
oldEl.replaceWith(newEl);
// ๋ถ๋ชจ๋ฅผ ํตํด ๊ต์ฒด
parent.replaceChild(newEl, oldEl);
์ด๋ฒคํธ ๊ธฐ์ด¶
์ด๋ฒคํธ ๋ฆฌ์ค๋ ๋ฑ๋ก¶
const button = document.querySelector('button');
// addEventListener (๊ถ์ฅ)
button.addEventListener('click', function(event) {
console.log('ํด๋ฆญ๋จ!');
});
// ํ์ดํ ํจ์
button.addEventListener('click', (e) => {
console.log('ํด๋ฆญ๋จ!');
});
// ํธ๋ค๋ฌ ๋ถ๋ฆฌ
function handleClick(event) {
console.log('ํด๋ฆญ๋จ!');
}
button.addEventListener('click', handleClick);
์ด๋ฒคํธ ๋ฆฌ์ค๋ ์ ๊ฑฐ¶
// ๊ฐ์ ํจ์ ์ฐธ์กฐ ํ์
function handleClick(event) {
console.log('ํด๋ฆญ๋จ!');
}
button.addEventListener('click', handleClick);
button.removeEventListener('click', handleClick);
// ์ต๋ช
ํจ์๋ ์ ๊ฑฐ ๋ถ๊ฐ
button.addEventListener('click', () => {}); // ์ ๊ฑฐ ๋ถ๊ฐ
์ด๋ฒคํธ ๊ฐ์ฒด¶
button.addEventListener('click', function(event) {
// ์ด๋ฒคํธ ์ ๋ณด
event.type; // "click"
event.target; // ์ค์ ํด๋ฆญ๋ ์์
event.currentTarget; // ๋ฆฌ์ค๋๊ฐ ๋ฑ๋ก๋ ์์
event.timeStamp; // ์ด๋ฒคํธ ๋ฐ์ ์๊ฐ
// ๋ง์ฐ์ค ์์น
event.clientX; // ๋ทฐํฌํธ ๊ธฐ์ค X
event.clientY; // ๋ทฐํฌํธ ๊ธฐ์ค Y
event.pageX; // ๋ฌธ์ ๊ธฐ์ค X
event.pageY; // ๋ฌธ์ ๊ธฐ์ค Y
// ํค๋ณด๋ ์ ๋ณด
event.key; // "Enter", "a", "Escape" ๋ฑ
event.code; // "Enter", "KeyA", "Escape" ๋ฑ
event.shiftKey; // Shift ๋๋ ธ๋์ง
event.ctrlKey; // Ctrl ๋๋ ธ๋์ง
event.altKey; // Alt ๋๋ ธ๋์ง
event.metaKey; // Cmd(Mac)/Win ๋๋ ธ๋์ง
});
๊ธฐ๋ณธ ๋์ ๋ฐฉ์ง¶
// ๋งํฌ ํด๋ฆญ ์ ์ด๋ ๋ฐฉ์ง
link.addEventListener('click', function(event) {
event.preventDefault();
console.log('์ด๋ํ์ง ์์');
});
// ํผ ์ ์ถ ๋ฐฉ์ง
form.addEventListener('submit', function(event) {
event.preventDefault();
console.log('์ ์ถํ์ง ์์');
});
์ด๋ฒคํธ ์ ํ ์ค์ง¶
// ๋ฒ๋ธ๋ง ์ค์ง
inner.addEventListener('click', function(event) {
event.stopPropagation();
// ์์ ์์๋ก ์ด๋ฒคํธ ์ ํ๋์ง ์์
});
// ๊ฐ์ ์์์ ๋ค๋ฅธ ํธ๋ค๋ฌ๋ ์ค์ง
inner.addEventListener('click', function(event) {
event.stopImmediatePropagation();
});
์ด๋ฒคํธ ์ต์ ¶
element.addEventListener('click', handler, {
once: true, // ํ ๋ฒ๋ง ์คํ ํ ์ ๊ฑฐ
capture: true, // ์บก์ฒ ๋จ๊ณ์์ ์คํ
passive: true // preventDefault ํธ์ถ ์ ํจ (์คํฌ๋กค ์ฑ๋ฅ)
});
// ์บก์ฒ ๋จ๊ณ
element.addEventListener('click', handler, true);
์ด๋ฒคํธ ํ๋ฆ¶
์บก์ฒ ๋จ๊ณ ๋ฒ๋ธ๋ง ๋จ๊ณ
(1) (4)
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ document โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ parent (2) (3) โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ target ํด๋ฆญ! โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
์ด๋ฒคํธ ์ข ๋ฅ¶
๋ง์ฐ์ค ์ด๋ฒคํธ¶
// ํด๋ฆญ
element.addEventListener('click', handler); // ํด๋ฆญ
element.addEventListener('dblclick', handler); // ๋๋ธํด๋ฆญ
element.addEventListener('contextmenu', handler); // ์ฐํด๋ฆญ
// ๋ง์ฐ์ค ๋ฒํผ
element.addEventListener('mousedown', handler); // ๋ฒํผ ๋๋ฆ
element.addEventListener('mouseup', handler); // ๋ฒํผ ๋
// ๋ง์ฐ์ค ์ด๋
element.addEventListener('mousemove', handler); // ์ด๋
element.addEventListener('mouseenter', handler); // ์์ ์ง์
(๋ฒ๋ธ๋ง X)
element.addEventListener('mouseleave', handler); // ์์ ์ดํ (๋ฒ๋ธ๋ง X)
element.addEventListener('mouseover', handler); // ์์ ์๋ก (๋ฒ๋ธ๋ง O)
element.addEventListener('mouseout', handler); // ์์ ๋ฒ์ด๋จ (๋ฒ๋ธ๋ง O)
// ๋ง์ฐ์ค ๋ฒํผ ํ์ธ
element.addEventListener('mousedown', (e) => {
e.button; // 0: ์ขํด๋ฆญ, 1: ํ , 2: ์ฐํด๋ฆญ
});
ํค๋ณด๋ ์ด๋ฒคํธ¶
// ํค ์ด๋ฒคํธ
document.addEventListener('keydown', handler); // ํค ๋๋ฆ
document.addEventListener('keyup', handler); // ํค ๋
document.addEventListener('keypress', handler); // ๋ฌธ์ ํค (deprecated)
// ํค ํ์ธ
document.addEventListener('keydown', (e) => {
console.log(e.key); // "a", "Enter", "Escape"
console.log(e.code); // "KeyA", "Enter", "Escape"
// ํน์ ํค
if (e.key === 'Enter') { }
if (e.key === 'Escape') { }
if (e.key === 'ArrowUp') { }
if (e.key === 'ArrowDown') { }
// ์กฐํฉ ํค
if (e.ctrlKey && e.key === 's') {
e.preventDefault();
console.log('์ ์ฅ');
}
});
ํผ ์ด๋ฒคํธ¶
// ์
๋ ฅ
input.addEventListener('input', handler); // ๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค
input.addEventListener('change', handler); // ํฌ์ปค์ค ์์ ๋ (๊ฐ ๋ณ๊ฒฝ ์)
// ํฌ์ปค์ค
input.addEventListener('focus', handler); // ํฌ์ปค์ค ๋ฐ์
input.addEventListener('blur', handler); // ํฌ์ปค์ค ์์
input.addEventListener('focusin', handler); // ํฌ์ปค์ค ๋ฐ์ (๋ฒ๋ธ๋ง O)
input.addEventListener('focusout', handler); // ํฌ์ปค์ค ์์ (๋ฒ๋ธ๋ง O)
// ์ ์ถ
form.addEventListener('submit', handler);
form.addEventListener('reset', handler);
์คํฌ๋กค/๋ฆฌ์ฌ์ด์ฆ ์ด๋ฒคํธ¶
// ์คํฌ๋กค
window.addEventListener('scroll', handler);
element.addEventListener('scroll', handler);
// ์คํฌ๋กค ์์น
window.addEventListener('scroll', () => {
console.log(window.scrollY); // ์์ง ์คํฌ๋กค ์์น
console.log(window.scrollX); // ์ํ ์คํฌ๋กค ์์น
});
// ๋ฆฌ์ฌ์ด์ฆ
window.addEventListener('resize', handler);
window.addEventListener('resize', () => {
console.log(window.innerWidth);
console.log(window.innerHeight);
});
// ์ฑ๋ฅ ์ต์ ํ: throttle/debounce ํ์
๋ก๋ ์ด๋ฒคํธ¶
// ๋ฌธ์ ๋ก๋
window.addEventListener('load', handler); // ๋ชจ๋ ๋ฆฌ์์ค ๋ก๋ ํ
document.addEventListener('DOMContentLoaded', handler); // DOM ํ์ฑ ์๋ฃ ์
// ๊ถ์ฅ ํจํด
document.addEventListener('DOMContentLoaded', () => {
// DOM ์กฐ์ ์ฝ๋
});
// ๋๋ defer ์คํฌ๋ฆฝํธ ์ฌ์ฉ
// <script src="main.js" defer></script>
// ์ด๋ฏธ์ง ๋ก๋
img.addEventListener('load', handler);
img.addEventListener('error', handler);
// ํ์ด์ง ์ดํ
window.addEventListener('beforeunload', (e) => {
e.preventDefault();
e.returnValue = ''; // ํ์ธ ๋ํ์์ ํ์
});
ํฐ์น ์ด๋ฒคํธ¶
element.addEventListener('touchstart', handler); // ํฐ์น ์์
element.addEventListener('touchmove', handler); // ํฐ์น ์ด๋
element.addEventListener('touchend', handler); // ํฐ์น ์ข
๋ฃ
element.addEventListener('touchcancel', handler); // ํฐ์น ์ทจ์
// ํฐ์น ์ ๋ณด
element.addEventListener('touchstart', (e) => {
const touch = e.touches[0];
console.log(touch.clientX, touch.clientY);
});
์ด๋ฒคํธ ์์¶
๊ฐ๋ ¶
๋ถ๋ชจ ์์์ ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ๋ฑ๋กํ์ฌ ์์ ์์์ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค.
<ul id="list">
<li data-id="1">ํญ๋ชฉ 1</li>
<li data-id="2">ํญ๋ชฉ 2</li>
<li data-id="3">ํญ๋ชฉ 3</li>
<!-- ๋์ ์ผ๋ก ์ถ๊ฐ๋๋ ํญ๋ชฉ๋ค... -->
</ul>
// ๋์ ์: ๊ฐ ์์์ ๋ฆฌ์ค๋ ๋ฑ๋ก
document.querySelectorAll('#list li').forEach(li => {
li.addEventListener('click', handleClick);
});
// ์ข์ ์: ๋ถ๋ชจ์ ์ด๋ฒคํธ ์์
document.querySelector('#list').addEventListener('click', (e) => {
// ํด๋ฆญ๋ ์์๊ฐ li์ธ์ง ํ์ธ
if (e.target.tagName === 'LI') {
console.log('ํด๋ฆญ๋ ํญ๋ชฉ:', e.target.dataset.id);
}
// ๋๋ closest ์ฌ์ฉ
const li = e.target.closest('li');
if (li) {
console.log('ํด๋ฆญ๋ ํญ๋ชฉ:', li.dataset.id);
}
});
์ด๋ฒคํธ ์์์ ์ฅ์ ¶
- ๋ฉ๋ชจ๋ฆฌ ํจ์จ: ๋ฆฌ์ค๋ ์ ๊ฐ์
- ๋์ ์์: ๋์ค์ ์ถ๊ฐ๋ ์์๋ ์ฒ๋ฆฌ
- ๊ฐ๋จํ ๊ด๋ฆฌ: ํ๋์ ๋ฆฌ์ค๋๋ก ๊ด๋ฆฌ
์ค์ ์์ ¶
// Todo ๋ฆฌ์คํธ
const todoList = document.querySelector('#todo-list');
todoList.addEventListener('click', (e) => {
const target = e.target;
const todoItem = target.closest('.todo-item');
if (!todoItem) return;
// ์๋ฃ ์ฒดํฌ
if (target.matches('.checkbox')) {
todoItem.classList.toggle('completed');
}
// ์ญ์ ๋ฒํผ
if (target.matches('.delete-btn')) {
todoItem.remove();
}
// ํธ์ง ๋ฒํผ
if (target.matches('.edit-btn')) {
const text = todoItem.querySelector('.text');
text.contentEditable = 'true';
text.focus();
}
});
ํผ ์ฒ๋ฆฌ¶
ํผ ์์ ์ ๊ทผ¶
const form = document.querySelector('#myForm');
// name์ผ๋ก ์ ๊ทผ
form.username; // name="username"์ธ ์์
form.elements.username; // ๊ฐ์
form.elements['user-name']; // ํ์ดํ ํฌํจ ์
// ๋ชจ๋ ์์
form.elements; // HTMLFormControlsCollection
form.elements.length; // ์์ ๊ฐ์
์ ๋ ฅ๊ฐ ๊ฐ์ ธ์ค๊ธฐ¶
// text, password, email, textarea
const textValue = input.value;
// checkbox
const isChecked = checkbox.checked;
// radio
const radioGroup = document.querySelectorAll('input[name="gender"]');
let selectedValue;
radioGroup.forEach(radio => {
if (radio.checked) selectedValue = radio.value;
});
// ๋๋
const selected = document.querySelector('input[name="gender"]:checked');
// select
const selectValue = select.value;
const selectedIndex = select.selectedIndex;
const selectedOption = select.options[select.selectedIndex];
// select multiple
const selectedOptions = [...select.selectedOptions].map(opt => opt.value);
// file
const files = fileInput.files;
const firstFile = files[0];
ํผ ์ด๋ฒคํธ ์ฒ๋ฆฌ¶
const form = document.querySelector('#myForm');
// ์ ์ถ
form.addEventListener('submit', (e) => {
e.preventDefault();
// FormData๋ก ๋ชจ๋ ๊ฐ ์์ง
const formData = new FormData(form);
// ๊ฐ๋ณ ๊ฐ
formData.get('username');
// ๋ชจ๋ ๊ฐ ๊ฐ์ฒด๋ก
const data = Object.fromEntries(formData);
// ๋๋ ์ํ
for (const [key, value] of formData) {
console.log(key, value);
}
});
// ์
๋ ฅ ์ค์๊ฐ ๊ฒ์ฆ
input.addEventListener('input', (e) => {
const value = e.target.value;
if (value.length < 3) {
e.target.classList.add('error');
} else {
e.target.classList.remove('error');
}
});
// ๋ณ๊ฒฝ ๊ฐ์ง
input.addEventListener('change', (e) => {
console.log('๊ฐ ๋ณ๊ฒฝ:', e.target.value);
});
ํผ ์ ํจ์ฑ ๊ฒ์ฌ¶
const form = document.querySelector('#myForm');
const email = document.querySelector('#email');
form.addEventListener('submit', (e) => {
// HTML5 ์ ํจ์ฑ ๊ฒ์ฌ
if (!form.checkValidity()) {
e.preventDefault();
form.reportValidity(); // ์๋ฌ ๋ฉ์์ง ํ์
return;
}
// ๊ฐ๋ณ ์์ ๊ฒ์ฌ
if (!email.validity.valid) {
if (email.validity.valueMissing) {
console.log('์ด๋ฉ์ผ ํ์');
}
if (email.validity.typeMismatch) {
console.log('์ด๋ฉ์ผ ํ์ ์ค๋ฅ');
}
}
});
// ์ปค์คํ
์๋ฌ ๋ฉ์์ง
email.addEventListener('invalid', (e) => {
e.target.setCustomValidity('์ฌ๋ฐ๋ฅธ ์ด๋ฉ์ผ์ ์
๋ ฅํ์ธ์');
});
email.addEventListener('input', (e) => {
e.target.setCustomValidity(''); // ์๋ฌ ๋ฉ์์ง ์ด๊ธฐํ
});
validity ์์ฑ¶
input.validity.valid // ์ ์ฒด ์ ํจ์ฑ
input.validity.valueMissing // required์ธ๋ฐ ๋น์ด์์
input.validity.typeMismatch // type ๋ถ์ผ์น (email, url ๋ฑ)
input.validity.patternMismatch // pattern ๋ถ์ผ์น
input.validity.tooLong // maxlength ์ด๊ณผ
input.validity.tooShort // minlength ๋ฏธ๋ฌ
input.validity.rangeOverflow // max ์ด๊ณผ
input.validity.rangeUnderflow // min ๋ฏธ๋ฌ
input.validity.stepMismatch // step ๋ถ์ผ์น
์ค์ ์์ ¶
ํญ ๋ฉ๋ด¶
<div class="tabs">
<div class="tab-buttons">
<button class="tab-btn active" data-tab="tab1">ํญ 1</button>
<button class="tab-btn" data-tab="tab2">ํญ 2</button>
<button class="tab-btn" data-tab="tab3">ํญ 3</button>
</div>
<div class="tab-content">
<div class="tab-panel active" id="tab1">๋ด์ฉ 1</div>
<div class="tab-panel" id="tab2">๋ด์ฉ 2</div>
<div class="tab-panel" id="tab3">๋ด์ฉ 3</div>
</div>
</div>
const tabButtons = document.querySelector('.tab-buttons');
tabButtons.addEventListener('click', (e) => {
const button = e.target.closest('.tab-btn');
if (!button) return;
// ๋ฒํผ ํ์ฑํ
document.querySelectorAll('.tab-btn').forEach(btn => {
btn.classList.remove('active');
});
button.classList.add('active');
// ํจ๋ ํ์
const tabId = button.dataset.tab;
document.querySelectorAll('.tab-panel').forEach(panel => {
panel.classList.remove('active');
});
document.getElementById(tabId).classList.add('active');
});
๋ชจ๋ฌ¶
<button id="openModal">๋ชจ๋ฌ ์ด๊ธฐ</button>
<div class="modal" id="modal">
<div class="modal-overlay"></div>
<div class="modal-content">
<button class="modal-close">×</button>
<h2>๋ชจ๋ฌ ์ ๋ชฉ</h2>
<p>๋ชจ๋ฌ ๋ด์ฉ์
๋๋ค.</p>
</div>
</div>
const modal = document.getElementById('modal');
const openBtn = document.getElementById('openModal');
// ์ด๊ธฐ
openBtn.addEventListener('click', () => {
modal.classList.add('open');
document.body.style.overflow = 'hidden';
});
// ๋ซ๊ธฐ (์ด๋ฒคํธ ์์)
modal.addEventListener('click', (e) => {
if (e.target.matches('.modal-close') ||
e.target.matches('.modal-overlay')) {
modal.classList.remove('open');
document.body.style.overflow = '';
}
});
// ESC ํค๋ก ๋ซ๊ธฐ
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && modal.classList.contains('open')) {
modal.classList.remove('open');
document.body.style.overflow = '';
}
});
Todo ๋ฆฌ์คํธ¶
<div class="todo-app">
<form id="todo-form">
<input type="text" id="todo-input" placeholder="ํ ์ผ ์
๋ ฅ" required>
<button type="submit">์ถ๊ฐ</button>
</form>
<ul id="todo-list"></ul>
</div>
const form = document.getElementById('todo-form');
const input = document.getElementById('todo-input');
const list = document.getElementById('todo-list');
// ์ถ๊ฐ
form.addEventListener('submit', (e) => {
e.preventDefault();
const text = input.value.trim();
if (!text) return;
const li = document.createElement('li');
li.innerHTML = `
<input type="checkbox" class="todo-check">
<span class="todo-text">${text}</span>
<button class="todo-delete">์ญ์ </button>
`;
list.appendChild(li);
input.value = '';
input.focus();
});
// ์๋ฃ/์ญ์ (์ด๋ฒคํธ ์์)
list.addEventListener('click', (e) => {
const li = e.target.closest('li');
if (!li) return;
if (e.target.matches('.todo-check')) {
li.classList.toggle('completed', e.target.checked);
}
if (e.target.matches('.todo-delete')) {
li.remove();
}
});
์ฐ์ต ๋ฌธ์ ¶
๋ฌธ์ 1: ์์ฝ๋์ธ ๋ฉ๋ด¶
ํด๋ฆญํ๋ฉด ๋ด์ฉ์ด ์ด๋ฆฌ๊ณ ๋ซํ๋ ์์ฝ๋์ธ์ ๊ตฌํํ์ธ์.
์ ๋ต ๋ณด๊ธฐ
const accordion = document.querySelector('.accordion');
accordion.addEventListener('click', (e) => {
const header = e.target.closest('.accordion-header');
if (!header) return;
const item = header.parentElement;
const content = item.querySelector('.accordion-content');
// ๋ค๋ฅธ ํญ๋ชฉ ๋ซ๊ธฐ (์ ํ์ฌํญ)
document.querySelectorAll('.accordion-item').forEach(other => {
if (other !== item) {
other.classList.remove('open');
}
});
// ํ์ฌ ํญ๋ชฉ ํ ๊ธ
item.classList.toggle('open');
});
๋ฌธ์ 2: ๊ธ์ ์ ์นด์ดํฐ¶
textarea์ ์ ๋ ฅํ ๋ ์ค์๊ฐ์ผ๋ก ๊ธ์ ์๋ฅผ ํ์ํ์ธ์.
์ ๋ต ๋ณด๊ธฐ
const textarea = document.querySelector('textarea');
const counter = document.querySelector('.counter');
const maxLength = 200;
textarea.addEventListener('input', (e) => {
const length = e.target.value.length;
counter.textContent = `${length} / ${maxLength}`;
if (length > maxLength) {
counter.classList.add('error');
} else {
counter.classList.remove('error');
}
});
๋ค์ ๋จ๊ณ¶
- 08_JS_Async.md - Promise, async/await, fetch