-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[1주차] 고주희 미션 제출합니다. #1
base: main
Are you sure you want to change the base?
Changes from 17 commits
c8d73d9
54d1a35
03df37d
47018a6
9e2a5c9
d95c86f
ee9bde1
8cfea2a
782d345
c62faf4
8f56746
3bfd9c5
0ea1258
dc2afad
9ef7ec7
e4219ae
35790d0
1c3b08d
23b0c60
4519d52
50e36c3
cb315a4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,37 @@ | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>Vanilla Todo</title> | ||
<link rel="stylesheet" href="style.css" /> | ||
</head> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
<title>📚 TO DO LIST</title> | ||
<link rel="stylesheet" href="style.css" /> | ||
</head> | ||
|
||
<body> | ||
<div class="container"></div> | ||
</body> | ||
<script src="script.js"></script> | ||
<body> | ||
<div class="container"> | ||
<!-- <header>📚 투두리스트</header> --> | ||
<header> | ||
<img id="emoji" src="/images/emoji.png" alt="이모지" /> | ||
</header> | ||
<form class="input-box"> | ||
<input id="todo-input" autofocus placeholder="할 일을 입력하세요" /> | ||
<button type="submit" id="add-todo-btn">➕</button> | ||
</form> | ||
|
||
<section class="todo-list-box"> | ||
<h4 id="todo-list-title">📋 TO DO (0)</h4> | ||
<ul class="todo-list"> | ||
<!-- 할 일 추가될 곳 --> | ||
</ul> | ||
</section> | ||
<hr /> | ||
<section class="done-list-box"> | ||
<h4 id="done-list-title">💿 DONE (0)</h4> | ||
<ul class="done-list"> | ||
<!-- 완료된 일 추가될 곳 --> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. HTML 안에서 목록들이 추가될 곳에 주석으로 정리 해주셔서 |
||
</ul> | ||
</section> | ||
</div> | ||
<script src="/script.js"></script> | ||
</body> | ||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,114 @@ | ||
// 다들 화이팅!! ٩( *˙0˙*)۶ | ||
const addTodoBtn = document.getElementById("add-todo-btn"); | ||
const todoInput = document.getElementById("todo-input"); | ||
const doneList = document.querySelector(".done-list"); | ||
const todoListTitle = document.getElementById("todo-list-title"); | ||
const todoList = document.querySelector(".todo-list"); | ||
const doneListTitle = document.getElementById("done-list-title"); | ||
|
||
// localStorage에서 값을 가져오는 함수 | ||
function getLocalStorageItem(key) { | ||
return JSON.parse(localStorage.getItem(key)) || []; | ||
} | ||
|
||
// localStorage에 값을 새로 저장하는 함수 | ||
function setLocalStorageItem(key, data) { | ||
localStorage.setItem(key, JSON.stringify(data)); | ||
} | ||
Comment on lines
+8
to
+16
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. localStorage에서 값을 가져오고 저장하는 일이 빈번한데, 이를 getter와 setter 메서드로 정리해서 코드 안전성/유지보수성을 높인 부분이 좋습니다! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 처음 코드 작성 할 때부터 꼭 수정해봐야겠다고 생각했던 부분이라, 리팩토링 시 가장 우선순위로 두었던 것인데 드러난 것 같아 뿌듯합니다 🤩 |
||
|
||
function handleAddTodo() { | ||
let inputValue = todoInput.value; // 입력한 todo 집중 | ||
if (!inputValue) return alert("내용을 입력해주세요!"); // 입력된 todo가 존재하지 않는다면 아무것도 하지 않기 위함 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 공백 문자 입력을 방지한 부분이 좋습니다👍🏻👍🏻 |
||
|
||
const todos = getLocalStorageItem("todos"); // localstorage에 저장해둔 todo를 가져와서 객체로 (아직 아무것도 없다면 빈 배열로 초기화) | ||
todos.unshift(inputValue); // 입력된 todo를 추가 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
그 이유가 궁금하시다면,,, JS의 immutability(불변성) 관련된 문제인데 이 글에 자세히 나와 있어서 참고하시면 좋을 것 같아요! 간단히 설명드리자면, 배열의 값을 직접 변경할 경우 디버깅이 어렵고 의도치 않은 결과를 불러올 수 있기 때문에 배열의 값을 직접 변경하는 방식이 아닌 새로운 배열을 만들어 할당하는 방식을 추천합니다. 따라서 값을 직접 변경하는 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 맞습니다! 최신순을 위해 push에서 unshift로 수정했었습니다! 아직 성능 최적화를 위한 코드작성 방법은 잘 모르는 상황인데 자세한 조언 감사합니다 ㅎㅎ |
||
|
||
setLocalStorageItem("todos", todos); // update 된 객체를 localStorage 에도 update | ||
|
||
todoInput.value = ""; // todo input 창 초기화 | ||
renderTodos(); // todo 목록 새로고침 | ||
} | ||
|
||
addTodoBtn.addEventListener("click", handleAddTodo); // +버튼에 todo 추가하는 함수 연결 | ||
|
||
function renderTodos() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. renderTodos라는 직관적인 함수명을 통해 해당 함수가 가진 역할(html 요소가 화면에 표시되는 것)을 바로 파악할 수 있어 좋습니다!🙌 |
||
const todos = getLocalStorageItem("todos"); // 저장해두었던 todo 목록 가져오기 (아직 없다면 빈 배열로 초기화) | ||
|
||
todoList.innerHTML = ""; // 새로운 목록을 업데이트하기 위해 기존 목록 초기화 | ||
// 초기화하지 않고도 배열에 추가된 항목을 화면에 표시할 수 있지만 -> 중복 문제, 성능 문제 생길 수 있음 | ||
|
||
todos.forEach((todo, index) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
let li = document.createElement("li"); // todo 목록에 들어있는 각각의 값 마다 li 태그 붙이기 | ||
li.innerHTML = `<span onclick="handleAddDone(${index})">${todo}</span> <i class="delete-btn" onclick="handleDeleteTodoItem(${index})"></i>`; // handleAddDone 함수와 handleDeleteTodoItem의 인자로 index를 넘겨주는 기능을 한 번에 처리하기 위함 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 코드 관리 면에서 중요한 부분들을 많이 알아가는 것 같습니다! 이 부분도 참고하여 리팩토링 후 추가 커밋 하도록 하겠습니다 ㅎㅎ |
||
todoList.appendChild(li); // 클래스인 todo-list의 자식 요소로 추가 | ||
}); | ||
|
||
todoListTitle.textContent = `📋 TO DO (${todos.length})`; // todo 목록에 들어있는 값의 개수 | ||
} | ||
|
||
function handleDeleteTodoItem(index) { | ||
const todos = getLocalStorageItem("todos"); // localstorage에 저장해둔 todo를 가져와서 객체로 (아직 아무것도 없다면 빈 배열로 초기화) | ||
|
||
todos.splice(index, 1); // todos 배열 내의 index에 해당하는 값을 1개 삭제할 것 | ||
|
||
setLocalStorageItem("todos", todos); // update된 todos 배열 localStorage에 update | ||
|
||
renderTodos(); // todo 목록에서 하나를 삭제했으므로 목록 새로고침 | ||
} | ||
|
||
function handleAddDone(index) { | ||
const todos = getLocalStorageItem("todos"); // localstorage에 저장해둔 todo를 가져와서 객체로 (아직 아무것도 없다면 빈 배열로 초기화) | ||
const done = getLocalStorageItem("done"); // localstorage에 저장해둔 done를 가져와서 객체로 (아직 아무것도 없다면 빈 배열로 초기화) | ||
|
||
const newDone = [todos[index], ...done]; // 선택된 todo를 done 배열의 맨 앞에 새로 추가 | ||
const newTodos = todos.filter((_, i) => i !== index); // Done이 된 todo의 index를 제외한 항목들만 남겨두기 | ||
|
||
setLocalStorageItem("todos", newTodos); | ||
setLocalStorageItem("done", newDone); | ||
|
||
renderTodos(); // update 됐으므로 새로고침 | ||
renderDone(); // update 됐으므로 새로고침 | ||
} | ||
|
||
function renderDone() { | ||
const done = getLocalStorageItem("done"); // localStorage에 저장해둔 done 목록을 가져옴 | ||
|
||
doneList.innerHTML = ""; // 기존 목록 초기화 | ||
|
||
done.forEach((todo, index) => { | ||
const li = document.createElement("li"); // li 요소 생성 | ||
|
||
const span = document.createElement("span"); // span 요소 생성 | ||
span.textContent = todo; // todo 텍스트 추가 | ||
|
||
const deleteBtn = document.createElement("i"); // delete 버튼 생성 | ||
deleteBtn.classList.add("delete-btn"); | ||
deleteBtn.onclick = () => handleDeleteDoneItem(index); | ||
|
||
// li 요소에 span과 delete 버튼 추가 | ||
li.appendChild(span); | ||
li.appendChild(deleteBtn); | ||
|
||
doneList.appendChild(li); // doneList에 li 요소를 추가 | ||
}); | ||
|
||
// done 목록의 제목에 완료된 항목 수를 표시 | ||
doneListTitle.textContent = `💿 DONE (${done.length})`; | ||
} | ||
|
||
function handleDeleteDoneItem(index) { | ||
const done = getLocalStorageItem("done"); // localstorage에 저장해둔 done를 가져와서 객체로 (아직 아무것도 없다면 빈 배열로 초기화) | ||
const newDone = done.filter((_, i) => i !== index); | ||
|
||
setLocalStorageItem("done", newDone); // update된 done 목록 localStorage에도 update | ||
|
||
renderDone(); // update 하였으므로 새로고침 | ||
} | ||
|
||
document.addEventListener("DOMContentLoaded", () => { | ||
// 초기 렌더링 설정을 위한 코드 | ||
// DOM이 완전히 로드된 후에 초기화되도록 설정! | ||
// 로컬 저장소에서 가져온 데이터로 초기 상태 보여질 수 있도록 함 | ||
// 이 코드가 없다면 페이지 로드 시 초기 목록을 로드하는 데 실패할 수 있음 | ||
renderTodos(); | ||
renderDone(); | ||
}); | ||
Comment on lines
+134
to
+141
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 아주 좋습니다 👍🏻🎉 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,118 @@ | ||
/* 자유롭게 디자인 해 주세요! */ | ||
@font-face { | ||
font-family: "KyoboHand"; | ||
src: url("https://fastly.jsdelivr.net/gh/projectnoonnu/[email protected]/KyoboHand.woff") format("woff"); | ||
font-weight: normal; | ||
font-style: normal; | ||
} | ||
* { | ||
font-family: "KyoboHand"; | ||
} | ||
Comment on lines
+1
to
+9
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 웹 폰트 적용한 부분 조아용~~ |
||
html, | ||
body { | ||
width: 100vw; | ||
height: 100vh; | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
background: radial-gradient(circle at 50% 0, rgba(255, 38, 38, 0.5), rgba(255, 0, 0, 0) 70.71%), | ||
radial-gradient(circle at 6.7% 75%, rgba(30, 30, 255, 0.5), rgba(0, 0, 255, 0) 70.71%), | ||
radial-gradient(circle at 93.3% 75%, rgba(63, 255, 63, 0.287), rgba(6, 26, 6, 0) 70.71%) beige; | ||
} | ||
Comment on lines
+10
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 배경색 오색찬란해서 마음에 듭니다🌈 |
||
|
||
header { | ||
margin: 18px 18px 0; | ||
display: flex; | ||
justify-content: center; | ||
} | ||
|
||
.container { | ||
width: 360px; | ||
height: 600px; | ||
border-radius: 20px; | ||
background: white; | ||
box-shadow: 0 0 25px rgba(0, 0, 0, 0.25); | ||
} | ||
|
||
.input-box { | ||
padding: 18px; | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
} | ||
|
||
#todo-list-title, | ||
#done-list-title { | ||
margin: 18px; | ||
} | ||
|
||
.todo-list, | ||
.done-list { | ||
width: 310px; | ||
height: 156px; | ||
overflow-y: scroll; | ||
} | ||
|
||
.delete-btn { | ||
width: 13px; | ||
height: 13px; | ||
background-image: url(images/delete.png); | ||
background-size: cover; | ||
margin-left: 15px; | ||
display: inline-flex; | ||
border: none; | ||
cursor: pointer; | ||
} | ||
|
||
hr { | ||
border: 0; | ||
height: 1px; | ||
background: rgb(236, 234, 234); | ||
} | ||
|
||
#todo-input, | ||
#todo-input:focus { | ||
width: 80%; | ||
height: 60%; | ||
margin: 1%; | ||
padding: 5%; | ||
outline: none; | ||
/* border-radius: 15px; */ | ||
border: 1px solid white; | ||
border-bottom: 1px solid gray; | ||
} | ||
|
||
#add-todo-btn, | ||
#add-todo-btn:hover { | ||
height: 60%; | ||
margin: 1%; | ||
border: none; | ||
border-radius: 10px; | ||
background: none; | ||
cursor: pointer; | ||
} | ||
|
||
li, | ||
li:hover { | ||
margin: 0 16px 16px 16px; | ||
font-size: 16px; | ||
cursor: pointer; | ||
flex-direction: row; | ||
} | ||
|
||
/* 스크롤바의 폭 너비 */ | ||
.scrollbar::-webkit-scrollbar { | ||
width: 10px; | ||
} | ||
|
||
.scrollbar::-webkit-scrollbar-thumb { | ||
background: rgba(220, 20, 60); /* 스크롤바 색상 */ | ||
border-radius: 10px; /* 스크롤바 둥근 테두리 */ | ||
} | ||
|
||
.scrollbar::-webkit-scrollbar-track { | ||
background: rgba(220, 20, 60, 0.1); /*스크롤바 뒷 배경 색상*/ | ||
} | ||
Comment on lines
+107
to
+119
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 스크롤바 디자인까지 신경쓰시다니 멋져요 👏🏻
Comment on lines
+107
to
+119
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 오 스크롤바 디자인 방법 저도 알려주세요! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 스크롤바 구현 자체는 여러번 해보았지만 커스텀 디자인을 해본 경험은 적어서 이번 기회를 통해 많은 글들을 찾아보았는데 그 중 가장 상세한 설명이 있는 글인 것 같아 공유합니다! 같이 공부해요 🙌 |
||
|
||
#emoji { | ||
width: 200px; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이미지 너무 귀여워요☺️