heyday2024 님의 블로그
[사전캠프 퀘스트] TO DO LIST 만들기 본문
DOM( Document Object Model ) 이란?
HTML, XML 문서의 프로그래밍 interface로, 쉽게 말해 HTML문서를 브라우저가 이해할 수 있도록 만든 Tree 자료구조!
- DOM은 nodes와 objects로 문서를 표현함.
할 일을 추가할 수 있는 투두리스트 웹페이지.
https://todolistforonlytoday.netlify.app/
투두리스트
todolistforonlytoday.netlify.app
- 할일을 입력창에 입력하고,추가버튼(+)을 누르면 할일이 추가됨.
- 추가된 항목은 delete버튼, 할일 내용, 체크박스를 포함하고 있음.
- delete 버튼을 누르면 항목 사라짐.
const inputArea = document.getElementById("text-list"); const addBtn = document.querySelector(".add-btn"); const listBoard = document.querySelector(".list"); addBtn.addEventListener("click", function () { const inputText = inputArea.value.trim(); if ((inputText === "")) { // 사용자가 어떤 내용도 적지 않고, 추가하기 버튼 눌렀을 때 alert("해야 할 일을 입력해주세요."); // 그리고 아무 추가적인 동작도 하지 않도록 리턴해주기 return; } //새로운 listed-item 요소 만들고, 클래스 이름 listed-item으로 설정 const newItem = document.createElement("div"); newItem.classList.add("listed-item"); // 고유한 id를 생성하는 법: 시간(날짜)은 계속 흐르니까 계속 바뀜. 즉, 시간(+날짜)을 이용해 고유한 아이디 만들기 좋음. const newId = Date.now(); //시간 기반 unique ID 생성 const checkbox = document.createElement("input"); checkbox.type = "checkbox"; checkbox.id = newId; // name은 아마 데이터를 백엔드로 보낼때 사용. 지금은 사용하지 않지만, 그래도 만들었음. checkbox.name = `to-do-${newId}`; const label = document.createElement("label"); // label의 for을 id로 연결함으로서, label 즉 보여지는 text를 클릭해도 checked 처리 되게하기. label.htmlFor = newId; label.classList.add("tasks"); //입력한 내용 버튼 누르면 내용 그대로 추가되게함 label.textContent = inputText; // 삭제버튼 생성 const deleteButton = document.createElement("button"); deleteButton.type = "button"; deleteButton.classList.add( "btn", "btn-outline-secondary", "delete-btn", "material-symbols-outlined" ); deleteButton.textContent = "delete"; // 삭제 버튼 클릭 시 항목 삭제 deleteButton.addEventListener("click", function () { listBoard.removeChild(newItem); // 해당 항목 삭제 }); // 새로운 항목을 listBoard에 추가 newItem.appendChild(checkbox); newItem.appendChild(label); newItem.appendChild(deleteButton); listBoard.appendChild(newItem); // 입력 필드 초기화 inputArea.value = ""; });
- trim(): 문자열의 양쪽 끝에서 공백 문자(스페이스, 탭, 줄바꿈 등)를 제거하는 메서드. 문자열 내부의 공백은 제거하지 않음.
- createElement(): HTML 문서에 새로운 요소를 동적으로 생성하는 메서드. 메서드를 호출할 때 태그 이름을 인자로 전달하여 해당 태그의 새로운 DOM 요소를 만들어냄.
- classList.add(): 선택한 요소의 class 목록에 새로운 클래스를 추가하는 메서드. 여러 개의 클래스를 동시에 추가 가능.
- Date.now(): 현재 시간을 밀리초 단위로 반환하는 정적 메서드. 고유한 ID를 생성하거나, 시간 기반의 값을 계산할 때 사용.
- removeChild(): DOM에서 선택한 요소의 자식 노드를 제거하는 메서드.
- appendChild(): 선택한 요소에 자식 노드를 마지막에 추가하는 메서드. 추가되는 노드는 반드시 DOM 요소여야함.
- 체크 박스 디자인을 좀 더 보기 좋게 바꿨고, label 또는 체크박스 클릭 시 task가 완료됨을 보여줌(line-through)
- 체크박스
- id: HTML 요소를 유일하게 식별하는 데 사용(체크박스를 식별하는 유일한 식별자)
- name: 주로 폼(form) 안에서 데이터를 서버로 보낼 때 사용. 같은 name을 가진 여러 요소가 있을 수 있으며, 서버에 전송될 때 해당 요소들의 값이 name 속성을 기준으로 전달됨.
- for: label 태그의 for 속성은 그 레이블(label)이 어떤 폼 요소와 연결되는지를 지정. for 속성에 들어가는 값은 연결하려는 폼 요소의 id 값과 동일해야함
- 이렇게 연결하면, 사용자가 해당 레이블을 클릭할 때 자동으로 그 폼 요소가 선택됨!!
- 체크박스
<input type="checkbox" id="1" name="to-do1" checked />
<label class="tasks" for="1">해야할 일1</label>
- 체크박스 디자인 바꾸기
input[type="checkbox"] {
display: none;
}
input[type="checkbox"] + label {
display: inline-block;
position: relative;
padding-left: 30px;
cursor: pointer;
}
input[type="checkbox"] + label::before {
display: block;
content: "";
font-size: 20px;
width: 20px;
height: 20px;
line-height: 20px;
text-align: center;
position: absolute;
left: 0;
top: 6px;
border: 1px solid #ddd;
color: lightcoral;
}
input[type="checkbox"]:checked + label::before {
content: "✔";
}
+ `display: none;`으로 기존 체크박스 안보이게하기.
+ `+`: 인접 형제 결합자(adjacent sibling combinator)로, 특정 요소 바로 뒤에 오는 형제요소 선택 시 사용
+ `::before`: 가상요소로, **무조건 `content`요소 속성을 사용해야함**. 이 속성으로 삽입할 텍스트를 지정하고 이를 선택한 요소의 내용 앞에 추가함. 실제 HTML문서에 존재하지 않지만, 시각적으로 요소 앞에 추가된 내용처럼 보임.
+ `inline-block`: inline-block으로 지정된 엘리먼트는 기본적으로 inline 엘리먼트처럼 한 줄에 다른 엘리먼트들과 배치된다. 하지만 inline 엘리먼트에서 불가능하던 width와 height 속성 지정 및 margin과 padding 속성의 상하 간격 지정이 가능
- 글 중앙에 선 긋기
input[type="checkbox"]:checked + label {
text-decoration: line-through;
/* 글 중앙에 선 긋는 효과 넣어서 task 완료 표시해주기 */
}
- checkbox가 checked되면, 그 인접형제요소인 label에 중앙선 효과 적용됨.
Html
<!DOCTYPE html>
<html lang="kr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>투두리스트</title>
<!-- 부트스트랩 -->
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
crossorigin="anonymous"
/>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
crossorigin="anonymous"
></script>
<!-- 구글 아이콘 써보기 -->
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0"
/>
<link rel="stylesheet" href="./main.css" />
<script defer src="./main.js"></script>
</head>
<body>
<div class="top">
<h1>TO DO LIST</h1>
</div>
<div class="container">
<div class="button_area">
<div class="add">
<input
type="text"
id="text-list"
placeholder="해야 할 일을 적어주세요."
/>
<button
type="button"
class="btn btn-outline-secondary add-btn material-symbols-outlined"
>
add
</button>
</div>
</div>
<div class="list">
<!-- <div class="listed-item">
<input type="checkbox" id="1" name="to-do1" checked />
<label class="tasks" for="1">해야할 일1</label>
<button
type="button"
class="btn btn-outline-secondary delete-btn material-symbols-outlined"
>
delete
</button>
</div> -->
</div>
</div>
</body>
</html>
CSS
@import url("https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@100..900&display=swap");
* {
font-family: "Noto Sans KR", sans-serif;
font-optical-sizing: auto;
font-weight: 400;
font-style: normal;
}
body {
background-image: url("https://img.freepik.com/premium-vector/graph-paper-texture-blank-note-paper_391639-2862.jpg");
display: flex;
justify-content: center;
/* 부모요소의 주 축(가로축)에서 자식 요소들을 가운데로 정렬 */
align-items: center;
/* 교차 축에서의 정렬(세로축 정렬) */
flex-direction: column;
padding: 50px 0;
margin: 0 40px;
}
h1 {
font-weight: 700;
font-size: 80px;
}
.container {
display: flex;
justify-content: center;
align-items: center;
padding: 30px 0;
flex-direction: column;
}
.button_area {
display: flex;
width: 100%;
padding-bottom: 40px;
/* background-color: #f8f2df; */
justify-content: center;
}
#text-list {
border-radius: 5px;
border: solid 1px lightslategray;
margin-right: 5px;
padding-left: 10px;
width: 60vb;
height: 100%;
font-size: 30px;
background-color: rgb(248, 248, 234);
}
.add-btn {
height: 100%;
font-size: 30px;
background-color: rgb(252, 239, 196);
margin-top: -16px;
}
.listed-item {
background-color: rgb(252, 245, 210);
border-radius: 10px;
padding: 10px 10px;
width: fit-content;
font-size: 20px;
margin-bottom: 20px;
}
input[type="checkbox"] {
display: none;
}
input[type="checkbox"] + label {
display: inline-block;
position: relative;
padding-left: 30px;
cursor: pointer;
}
input[type="checkbox"] + label::before {
display: block;
content: "";
font-size: 20px;
width: 20px;
height: 20px;
line-height: 20px;
text-align: center;
position: absolute;
left: 0;
top: 6px;
border: 1px solid #ddd;
color: lightcoral;
}
input[type="checkbox"]:checked + label::before {
content: "✔";
}
input[type="checkbox"]:checked + label {
text-decoration: line-through;
/* 글 중앙에 선 긋는 효과 넣어서 task 완료 표시해주기 */
}
.delete-btn {
padding: 5px;
margin-left: 15px;
}
JS
const inputArea = document.getElementById("text-list");
const addBtn = document.querySelector(".add-btn");
const listBoard = document.querySelector(".list");
addBtn.addEventListener("click", function () {
const inputText = inputArea.value.trim();
if ((inputText === "")) {
// 사용자가 어떤 내용도 적지 않고, 추가하기 버튼 눌렀을 때
alert("해야 할 일을 입력해주세요.");
// 그리고 아무 추가적인 동작도 하지 않도록 리턴해주기
return;
}
//새로운 listed-item 요소 만들고, 클래스 이름 listed-item으로 설정
const newItem = document.createElement("div");
newItem.classList.add("listed-item");
// 고유한 id를 생성하는 법: 시간(날짜)은 계속 흐르니까 계속 바뀜. 즉, 시간(+날짜)을 이용해 고유한 아이디 만들기 좋음.
const newId = Date.now(); //시간 기반 unique ID 생성
const checkbox = document.createElement("input");
checkbox.type = "checkbox";
checkbox.id = newId;
// name은 아마 데이터를 백엔드로 보낼때 사용. 지금은 사용하지 않지만, 그래도 만들었음.
checkbox.name = `to-do-${newId}`;
const label = document.createElement("label");
// label의 for을 id로 연결함으로서, label 즉 보여지는 text를 클릭해도 checked 처리 되게하기.
label.htmlFor = newId;
label.classList.add("tasks");
//입력한 내용 버튼 누르면 내용 그대로 추가되게함
label.textContent = inputText;
// 삭제버튼 생성
const deleteButton = document.createElement("button");
deleteButton.type = "button";
deleteButton.classList.add(
"btn",
"btn-outline-secondary",
"delete-btn",
"material-symbols-outlined"
);
deleteButton.textContent = "delete";
// 삭제 버튼 클릭 시 항목 삭제
deleteButton.addEventListener("click", function () {
listBoard.removeChild(newItem); // 해당 항목 삭제
});
// 새로운 항목을 listBoard에 추가
newItem.appendChild(checkbox);
newItem.appendChild(label);
newItem.appendChild(deleteButton);
listBoard.appendChild(newItem);
// 입력 필드 초기화
inputArea.value = "";
});
달리기반 끝!

'프론트엔드 부트캠프' 카테고리의 다른 글
[사전캠프 퀘스트] 로또 번호 생성기 (0) | 2024.09.21 |
---|---|
[사전캠프 4주차] Firebase (0) | 2024.09.19 |
[사전캠프 퀘스트] 한식 메뉴 렌더링하기 (1) | 2024.09.14 |
[사전캠프 퀘스트] 숫자 기억 게임 만들기 (8) | 2024.09.12 |
[사전캠프 3주차] Fetch 여기저기 사용해보기 (0) | 2024.09.11 |