(선수) JS/DOM/이벤트
실력 향상 Tip
초기 동작에 만족하지 말고, 더 나은 코드를 지속적으로 고민하고 개선하세요.
click 이벤트 리스너
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>DOM</title>
</head>
<body>
<h1>hello world</h1>
<button id="btn">클릭햇!</button>
<script>
const heading = document.querySelector('h1')
const btn = document.querySelector('#btn')
// 노드.addEventListener(이벤트이름, 함수)
btn.addEventListener('click', function(e){
alert('버튼을 클릭했습니다!')
})
heading.addEventListener('click', e => {
heading.style.color = 'red'
console.log(e)
})
</script>
</body>
</html>
JS를 통한 DOM 생성/추가
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>DOM</title>
</head>
<body>
<p>hello</p>
<script>
const data = [
{ name: '키링', price: 1000 },
{ name: '플스5', price: 500000 },
{ name: '오븐', price: 100000 },
{ name: '곤충채집통', price: 15000 },
]
const p1 = document.createElement('p')
p1.textContent = data[0].name
document.body.appendChild(p1)
const p2 = document.createElement('p')
p2.textContent = data[1].name
document.body.appendChild(p2)
const p3 = document.createElement('p')
p3.textContent = data[2].name
document.body.appendChild(p3)
const p4 = document.createElement('p')
p4.textContent = data[3].name
document.body.appendChild(p4)
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>DOM</title>
</head>
<body>
<p>hello</p>
<script>
const data = [
{ name: '키링', price: 1000 },
{ name: '플스5', price: 500000 },
{ name: '오븐', price: 100000 },
{ name: '곤충채집통', price: 15000 },
]
// 각 항목의 name을 <p>로 감싸는 HTML 문자열 생성
const html = data.map(item => `<p>${item.name}</p>`).join('')
// body에 추가
document.body.innerHTML += html
</script>
</body>
</html>
폼 제출 이벤트
이벤트의 기본동작을 막고, JS를 통해 다른 로직 수행하기
form submit 기본 동작
페이지가 전환되며, action 지정 주소로 입력 데이터 전송하고, 서버 응답으로 화면 전체를 다시 그리기
출처 코드를 변경했습니다.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>폼 제출 이벤트</title>
</head>
<body>
<form action="">
<input type="text" name="username" value="익명" />
<input type="password" name="password" />
<button type="submit" class="submit">로그인</button>
</form>
<div class="responseText"></div>
<script>
const submit = document.querySelector('.submit');
submit.addEventListener('click', (event) => {
event.preventDefault();
const username = document.querySelector("[name=username]").value;
const password = document.querySelector("[name=password]").value;
// 서버로 전송할 데이터 (파일 업로드 지원 불가)
const data = { username, password };
// TODO: 서버 응답 받기
const responseMessage = !! data.password ? "로그인 성공" : "암호를 입력하세요.";
const responseTextEl = document.querySelector('.responseText');
responseTextEl.innerHTML = responseMessage;
});
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<title>폼 제출 이벤트</title>
</head>
<body>
<form action=""
id="loginForm"
>
<input type="text" name="username" value="익명" />
<input type="password" name="password" />
<button type="submit">로그인</button>
</form>
<div id="responseText"></div>
<script>
const formEl = document.querySelector("#loginForm");
formEl.addEventListener("submit", (e) => {
e.preventDefault();
const _formEl = e.target;
const formData = new FormData(_formEl); // 파일 업로드 가능.
// TODO: 서버 응답 받기
const responseMessage = !! formData.get('password') ? "로그인 성공" : "암호를 입력하세요.";
const responseTextEl = document.querySelector('#responseText');
responseTextEl.innerHTML = responseMessage;
});
</script>
</body>
</html>
비동기 로딩 및 UI 반영
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style></style>
</head>
<body>
<h1>상품 목록</h1>
<ul id="product-list1"></ul>
<ul id="product-list2"></ul>
<script src="./fetch.js"></script>
<script src="./asyncAwait.js"></script>
</body>
</html>
const API_URL = "https://dev.wenivops.co.kr/services/fastapi-crud/1/product";
fetch(API_URL)
.then((response) => response.json())
.then((json) => {
const productList = document.getElementById("product-list1");
json.forEach((product) => {
const li = document.createElement("li");
// console.log(product);
li.textContent = `${product.productName}: ${product.price}`;
productList.appendChild(li);
});
})
.catch((error) => console.log(error));
asyncAwait.js
async function fetchProducts() {
try {
const response = await fetch(API_URL);
const json = await response.json();
// console.log("async-await: ", json);
const productList = document.getElementById("product-list2");
json.forEach((product) => {
const li = document.createElement("li");
// console.log(product);
li.textContent = `${product.productName}: ${product.price}`;
productList.appendChild(li);
});
} catch (error) {
console.log(error);
}
}
fetchProducts();
Warning
JS 함수 내에서의 DOM 하드코딩 접근은 함수의 재사용성을 낮출 수 있기에 주의해주세요.
- id는 웹페이지 내에서 유일해야 합니다. 같은 id의 요소가 2개 이상 있다면, 그 중 1개만 선택 가능.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Document</title>
<style></style>
</head>
<body>
<h1>상품 목록</h1>
<ul id="product-list1"></ul>
<ul id="product-list2"></ul>
<!-- <script src="./fetch.js"></script> -->
<!-- <script src="./asyncAwait.js"></script> -->
</body>
<!-- 특정 페이지 내에서 사용되는 JS라면, HTML에 포함시키는 것이 관리성이 낫습니다. 관련있는 코드는 가가운 곳에 -->
<!-- 물론 정답은 없고, 고민해보세요. -->
<script>
</script>
<script>
loadProducts1("#product-list1");
loadProducts2("#product-list2");
/* JSON 포맷 응답 : Client Side Rendering */
function loadProducts1(targetSelector) {
const targetEl = document.querySelector(targetSelector);
const API_URL = "https://raw.githubusercontent.com/pyhub-kr/dump-data/refs/heads/main/weniv/fastapi-crud/product.json";
// JSON 응답 => DOM 변환이 필요
fetch(API_URL)
.then((response) => response.json())
.then((json) => {
json.forEach((product) => {
const li = document.createElement("li");
li.textContent = `${product.productName}: ${product.price}`;
targetEl.appendChild(li);
});
})
.catch((error) => console.log(error));
}
/* HTML 포맷 응답 : Server Side Rendering의 한 종류 */
async function loadProducts2(targetSelector) {
const targetEl = document.querySelector(targetSelector);
const API_URL = "https://raw.githubusercontent.com/pyhub-kr/dump-data/refs/heads/main/weniv/fastapi-crud/product.html";
try {
const response = await fetch(API_URL);
// HTML 응답을 그대로 DOM에 반영
targetEl.innerHTML = await response.text();
} catch (error) {
console.log(error)
}
}
</script>
</html>