From 305b9fc8bbbfcf63b2db7309d8f57d6e1c9efa6f Mon Sep 17 00:00:00 2001 From: brian <90752841+wokbjso@users.noreply.github.com> Date: Fri, 15 Sep 2023 01:39:00 +0900 Subject: [PATCH 1/4] =?UTF-8?q?Feat:=ED=88=AC=EB=91=90=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EA=B5=AC=ED=98=84=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- css/board.css | 72 ++++++++++++++++ css/header.css | 219 +++++++++++++++++++++++++++++++++++++++++++++++++ index.html | 35 ++++++-- script.js | 0 script/todo.js | 97 ++++++++++++++++++++++ style.css | 0 6 files changed, 418 insertions(+), 5 deletions(-) create mode 100644 css/board.css create mode 100644 css/header.css delete mode 100644 script.js create mode 100644 script/todo.js delete mode 100644 style.css diff --git a/css/board.css b/css/board.css new file mode 100644 index 0000000..1861985 --- /dev/null +++ b/css/board.css @@ -0,0 +1,72 @@ +.boards { + width: 100%; + padding: 5rem 0; +} + +.board { + width: 100%; + font-size: 6rem; + display: flex; + flex-direction: column; + align-items: center; + margin-bottom: 8rem; + font-family: "GangwonState"; +} + +.boardName { + margin-bottom: 4rem; +} + +.boardInput { + width: 60rem; + height: 5rem; + padding: 1rem; + margin-right: 2rem; + font-size: 3.5rem; + border: none; + border-bottom: 1px solid transparent; + border-image: linear-gradient(to left, white, black); + border-image-slice: 1; + background-color: transparent; +} + +.todoWrapper { + display: flex; + align-items: center; + margin-bottom: 3rem; +} + +.addTodoList { + width: 8rem; + height: 7rem; + padding: 1rem; + font-size: 8rem; + display: flex; + justify-content: center; + align-items: center; + border: none; + background-color: transparent; +} + +.addListWrapper { + display: flex; + align-items: center; + margin-bottom: 5rem; +} + +.deleteBtn { + background-color: transparent; + display: flex; + justify-content: center; + align-items: center; + margin-left: 1rem; + border: none; + font-size: 4rem; + color: gray; +} + +.todoCheckbox { + margin-right: 1.2rem; + width: 3rem; + height: 3rem; +} diff --git a/css/header.css b/css/header.css new file mode 100644 index 0000000..712dd78 --- /dev/null +++ b/css/header.css @@ -0,0 +1,219 @@ +html, +body, +div, +span, +applet, +object, +iframe, +h1, +h2, +h3, +h4, +h5, +h6, +p, +blockquote, +pre, +a, +abbr, +acronym, +address, +big, +cite, +code, +del, +dfn, +em, +img, +ins, +kbd, +q, +s, +samp, +small, +strike, +strong, +sub, +sup, +tt, +var, +b, +u, +i, +center, +dl, +dt, +dd, +ol, +ul, +li, +fieldset, +form, +label, +legend, +table, +caption, +tbody, +tfoot, +thead, +tr, +th, +td, +article, +aside, +canvas, +details, +embed, +figure, +figcaption, +footer, +header, +hgroup, +menu, +nav, +output, +ruby, +section, +summary, +time, +mark, +audio, +video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} +/* HTML5 display-role reset for older browsers */ +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +menu, +nav, +section { + display: block; +} +body { + position: relative; + height: 100vh; + line-height: 1; +} +ol, +ul { + list-style: none; +} +blockquote, +q { + quotes: none; +} +blockquote:before, +blockquote:after, +q:before, +q:after { + content: ""; + content: none; +} +table { + border-collapse: collapse; + border-spacing: 0; +} + +html { + font-size: 10px; /* Default font size */ + + /* Media query for tablet */ + @media (min-width: 768px) and (max-width: 1023px) { + font-size: 8px; + } + + /* Media query for mobile */ + @media (min-width: 450px) and (max-width: 768px) { + font-size: 6px; + } + @media (max-width: 450px) { + font-size: 4px; + } +} + +@font-face { + font-family: "GangwonState"; + src: url("https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_2307-2@1.0/GangwonState.woff2") + format("woff2"); + font-weight: normal; + font-style: normal; +} + +@font-face { + font-family: "Giants-Inline"; + src: url("https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_2307-1@1.1/Giants-Inline.woff2") + format("woff2"); + font-weight: normal; + font-style: normal; +} + +.container { + padding-top: 6rem; + min-width: 370px; + height: 100vh; + background: linear-gradient(to right, #dddddd, #ffffff, #dddddd); +} + +header { + display: flex; + flex-direction: column; + align-items: center; + position: relative; + padding: 5rem; +} + +header > div { + font-size: 10rem; + font-weight: 500; + font-weight: 600; + margin-bottom: 4rem; + font-family: "Giants-Inline"; +} + +header > button { + font-size: 5rem; + position: absolute; + background: transparent; + border-radius: 1rem; + right: 0; +} + +.headerBtn { + width: 17rem; + height: 6rem; + padding: 2rem; + display: flex; + align-items: center; + border-radius: 1rem; +} + +.headerBtnWrapper { + font-size: 4rem; + display: flex; + align-items: center; + margin: 2rem 0; +} + +#totalTodo { + background-color: wheat; +} + +#nowTodo { + background-color: skyblue; + margin: 0 3rem; +} + +#doneTodo { + background-color: pink; +} diff --git a/index.html b/index.html index 420961c..ef96d09 100644 --- a/index.html +++ b/index.html @@ -3,12 +3,37 @@ - Vanilla Todo - + Todo List + + -
+
+
+
TO-DO LIST
+
+
+
+
+
+
+
+
+
+
+ + +
+
+
+
+
- - \ No newline at end of file + + + diff --git a/script.js b/script.js deleted file mode 100644 index e69de29..0000000 diff --git a/script/todo.js b/script/todo.js new file mode 100644 index 0000000..48b7ff7 --- /dev/null +++ b/script/todo.js @@ -0,0 +1,97 @@ +const todoInput = document.querySelector("#todoBoardInput"); +const addTodo = document.querySelector("#todoAdd"); +const todoList = document.querySelector("#todoList"); +const totalTodoCount = document.querySelector("#totalTodo"); +const nowTodo = document.querySelector("#nowTodo"); +const doneTodo = document.querySelector("#doneTodo"); + +const storedTodos = JSON.parse(localStorage.getItem("todos")) || []; + +function saveTodos() { + localStorage.setItem("todos", JSON.stringify(storedTodos)); +} + +function updateTotalTodoCount() { + totalTodoCount.textContent = "할 일: " + storedTodos.length; +} + +function updateCheckedTodoCount() { + const checkboxes = document.querySelectorAll(".todoCheckbox"); + const checkedCount = Array.from(checkboxes).filter( + (checkbox) => checkbox.checked + ).length; + + nowTodo.textContent = "진행중: " + (storedTodos.length - checkedCount); + doneTodo.textContent = "완료: " + checkedCount; +} + +function addTodoItem(todo) { + const todoItem = document.createElement("div"); + todoItem.classList.add("todoWrapper"); + + const checkbox = document.createElement("input"); + checkbox.type = "checkbox"; + checkbox.classList.add("todoCheckbox"); + checkbox.checked = todo.checked || false; + checkbox.addEventListener("change", () => { + todo.checked = checkbox.checked; + saveTodos(); + todoText.style.textDecoration = checkbox.checked ? "line-through" : "none"; + updateCheckedTodoCount(); + }); + + const todoText = document.createElement("div"); + todoText.textContent = todo.text; + todoText.classList.add("todoText"); + todoText.style.textDecoration = todo.checked ? "line-through" : "none"; + + const deleteButton = document.createElement("button"); + deleteButton.classList.add("deleteBtn"); + deleteButton.textContent = "X"; + deleteButton.addEventListener("click", () => { + const index = storedTodos.indexOf(todo); + storedTodos.splice(index, 1); + saveTodos(); + todoList.removeChild(todoItem); + updateTotalTodoCount(); + updateCheckedTodoCount(); + }); + + todoItem.appendChild(checkbox); + todoItem.appendChild(todoText); + todoItem.appendChild(deleteButton); + + todoList.appendChild(todoItem); + updateTotalTodoCount(); + updateCheckedTodoCount(); +} + +storedTodos.forEach((todo) => { + addTodoItem(todo); +}); + +addTodo.addEventListener("click", () => { + const text = todoInput.value.trim(); + if (text) { + const newTodo = { text, checked: false }; + storedTodos.push(newTodo); + saveTodos(); + addTodoItem(newTodo); + todoInput.value = ""; + } +}); + +todoInput.addEventListener("keydown", (event) => { + if (event.key === "Enter") { + const text = todoInput.value.trim(); + if (text) { + const newTodo = { text, checked: false }; + storedTodos.push(newTodo); + saveTodos(); + addTodoItem(newTodo); + todoInput.value = ""; + } + } +}); + +updateTotalTodoCount(); diff --git a/style.css b/style.css deleted file mode 100644 index e69de29..0000000 From a4433d0ffddc8dc5b11444cba7a0e0ef82f821ef Mon Sep 17 00:00:00 2001 From: brian <90752841+wokbjso@users.noreply.github.com> Date: Fri, 15 Sep 2023 02:02:57 +0900 Subject: [PATCH 2/4] =?UTF-8?q?Feat:=ED=88=AC=EB=91=90=20=EB=A6=AC?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EA=B5=AC=ED=98=84=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- css/header.css | 1 - 1 file changed, 1 deletion(-) diff --git a/css/header.css b/css/header.css index 712dd78..cecfaa8 100644 --- a/css/header.css +++ b/css/header.css @@ -96,7 +96,6 @@ footer, header, hgroup, menu, -nav, section { display: block; } From 8047059d480c6c18e4076e2cbc32f985c986eb1d Mon Sep 17 00:00:00 2001 From: brian <90752841+wokbjso@users.noreply.github.com> Date: Sat, 16 Sep 2023 15:43:30 +0900 Subject: [PATCH 3/4] =?UTF-8?q?Feat:=ED=88=AC=EB=91=90=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EA=B0=80=20100vh=EB=A5=BC=20=EB=84=98=EC=96=B4?= =?UTF-8?q?=EA=B0=88=20=EC=8B=9C=20=EB=B0=B0=EA=B2=BD=EC=83=89=EB=8F=84=20?= =?UTF-8?q?=EB=8A=98=EC=96=B4=EB=82=98=EA=B2=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- css/header.css | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/css/header.css b/css/header.css index cecfaa8..0cae6a0 100644 --- a/css/header.css +++ b/css/header.css @@ -101,7 +101,6 @@ section { } body { position: relative; - height: 100vh; line-height: 1; } ol, @@ -160,7 +159,7 @@ html { .container { padding-top: 6rem; min-width: 370px; - height: 100vh; + min-height: 100vh; background: linear-gradient(to right, #dddddd, #ffffff, #dddddd); } From 332b636ccee7f5214f7d9a173f872c64845c25d3 Mon Sep 17 00:00:00 2001 From: brian <90752841+wokbjso@users.noreply.github.com> Date: Sat, 16 Sep 2023 18:33:33 +0900 Subject: [PATCH 4/4] =?UTF-8?q?Chore:=20=EC=A3=BC=EC=84=9D=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- script/todo.js | 55 +++++++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/script/todo.js b/script/todo.js index 48b7ff7..e81eab9 100644 --- a/script/todo.js +++ b/script/todo.js @@ -5,26 +5,31 @@ const totalTodoCount = document.querySelector("#totalTodo"); const nowTodo = document.querySelector("#nowTodo"); const doneTodo = document.querySelector("#doneTodo"); +//접속 시 localStorage에 todo가 저장되어 있다면 parse 하여 불러오고, 없다면 빈 배열 저장 const storedTodos = JSON.parse(localStorage.getItem("todos")) || []; +//localStorage에 현재 todo 상태 저장하는 함수 function saveTodos() { localStorage.setItem("todos", JSON.stringify(storedTodos)); } +//현재 todo의 총 개수를 계산하는 함수 function updateTotalTodoCount() { totalTodoCount.textContent = "할 일: " + storedTodos.length; } +//현재 체크된 todo를 계산하여 진행중인 todo와 완료된 todo를 구분하는 함수 function updateCheckedTodoCount() { const checkboxes = document.querySelectorAll(".todoCheckbox"); const checkedCount = Array.from(checkboxes).filter( (checkbox) => checkbox.checked - ).length; + ).length; //만들어진 체크박스들을 배열로 불러온 뒤, 각각의 체크 상태를 판단하여 체크된 개수를 저장 nowTodo.textContent = "진행중: " + (storedTodos.length - checkedCount); doneTodo.textContent = "완료: " + checkedCount; } +//todo 추가 시 및 화면 재접속 시 각각의 todo 상태를 전달받아 화면에 띄워주는 함수 function addTodoItem(todo) { const todoItem = document.createElement("div"); todoItem.classList.add("todoWrapper"); @@ -32,55 +37,59 @@ function addTodoItem(todo) { const checkbox = document.createElement("input"); checkbox.type = "checkbox"; checkbox.classList.add("todoCheckbox"); - checkbox.checked = todo.checked || false; + checkbox.checked = todo.checked || false; //전달받은 todo의 check 상태가 true면 true, 아니면 false로 선언 checkbox.addEventListener("change", () => { - todo.checked = checkbox.checked; - saveTodos(); - todoText.style.textDecoration = checkbox.checked ? "line-through" : "none"; - updateCheckedTodoCount(); + todo.checked = checkbox.checked; //해당 todo의 체크박스 상태가 변하면 그 상태를 저장해 준 후 + saveTodos(); //localStorage의 체크박스 상태 변화 저장 + todoText.style.textDecoration = checkbox.checked ? "line-through" : "none"; //체크박스가 check 되어있다면 해당 todo에 줄 그어줌 + updateCheckedTodoCount(); //해당 todo의 체크 상태가 변했으므로 진행중인 todo와 완료된 todo개수 update }); const todoText = document.createElement("div"); - todoText.textContent = todo.text; + todoText.textContent = todo.text; //불러온 todo의 text 값을 생성한 요소에 저장 todoText.classList.add("todoText"); - todoText.style.textDecoration = todo.checked ? "line-through" : "none"; + todoText.style.textDecoration = todo.checked ? "line-through" : "none"; //불러온 todo의 체크 상태에 따라 줄을 그을지 말지 판단 const deleteButton = document.createElement("button"); deleteButton.classList.add("deleteBtn"); deleteButton.textContent = "X"; + //각 todo의 X 버튼을 누르면 trigger deleteButton.addEventListener("click", () => { - const index = storedTodos.indexOf(todo); - storedTodos.splice(index, 1); - saveTodos(); - todoList.removeChild(todoItem); - updateTotalTodoCount(); - updateCheckedTodoCount(); + const index = storedTodos.indexOf(todo); //전체 todo 상태가 저장되어 있는 배열에서 해당 todo의 index 값 불러옴 + storedTodos.splice(index, 1); //해당 index부터 1개의 요소 삭제(즉, 해당 todo만 삭제) + saveTodos(); //삭제된 상태를 localStorage 에 update + todoList.removeChild(todoItem); //해당 todo를 화면에서 지워줌 + updateTotalTodoCount(); //전체 todo 개수 update + updateCheckedTodoCount(); //진행중 및 완료 todo 개수 update }); todoItem.appendChild(checkbox); todoItem.appendChild(todoText); todoItem.appendChild(deleteButton); - todoList.appendChild(todoItem); - updateTotalTodoCount(); - updateCheckedTodoCount(); + todoList.appendChild(todoItem); // checkbox와 text, 삭제 버튼이 append된 todo 요소를 todo를 저장하는 list요소에 append + updateTotalTodoCount(); //전체 todo 개수 update + updateCheckedTodoCount(); //진행중 및 완료 todo 개수 update } +//화면 초기 접속 시 localStorage에 접근하여 불러온 배열의 각각 요소들을 선언한 addTodoItem에 전달 storedTodos.forEach((todo) => { addTodoItem(todo); }); +//입력버튼을 클릭 시 해당 함수가 trigger 되게 addEventListener 등록 addTodo.addEventListener("click", () => { - const text = todoInput.value.trim(); + const text = todoInput.value.trim(); //입력한 todo의 불필요한 공백 제거 if (text) { - const newTodo = { text, checked: false }; - storedTodos.push(newTodo); - saveTodos(); - addTodoItem(newTodo); - todoInput.value = ""; + const newTodo = { text, checked: false }; //입력된 text와, 초기에 check은 false이므로 해당 상태를 변수로 선언 + storedTodos.push(newTodo); //현재 todo를 저장하는 배열 변수에 push + saveTodos(); //push된 배열 변수의 상태를 localStorage에 update + addTodoItem(newTodo); //현재 입력한 todo의 상태를 화면에 보여주도록 선언한 addTodoItem에 전달 + todoInput.value = ""; //입력을 완료하면 현재 input 값 빈 string으로 선언 } }); +//PC에서의 편리한 사용을 고려하여 엔터를 눌러도 해당 함수가 trigger 되게 addEventListener 등록 todoInput.addEventListener("keydown", (event) => { if (event.key === "Enter") { const text = todoInput.value.trim();