-
Notifications
You must be signed in to change notification settings - Fork 10
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주차] 이지인 미션 제출합니다. #12
base: master
Are you sure you want to change the base?
Changes from all commits
19ea084
efa9783
60d33b6
6c038d0
d1f6521
99ad41a
3c19bcc
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,20 @@ | ||
<!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> | ||
<html lang="ko"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<title>ToDo List App</title> | ||
<link rel="stylesheet" href="style.css" /> | ||
</head> | ||
<body> | ||
<div id="app"> | ||
<div class="calendar-section"> | ||
<input type="date" id="datePicker" /> | ||
</div> | ||
<input type="text" id="sectionInput" placeholder="Category Name" /> | ||
<button id="addSectionBtn">카테고리 추가</button> | ||
<div id="todoSections"></div> | ||
</div> | ||
|
||
<body> | ||
<div class="container"></div> | ||
</body> | ||
<script src="script.js"></script> | ||
<script src="script.js"></script> | ||
</body> | ||
</html> |
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. div로 element를 많이 만들어주셨는데, 시맨틱 태그로 리팩토링 해주신다면 웹 페이지 접근성에 더 도움이 될 거 같아요~ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,150 @@ | ||
//CEOS 19기 프론트엔드 파이팅🔥 ദ്ദി˶ˊᵕˋ˵) | ||
|
||
document.addEventListener('DOMContentLoaded', function () { | ||
/* 조작할 DOM 요소 아이디로 참조 -> | ||
달력 / 섹션 추가 버튼 / 섹션명 입력 인풋 필드 / 섹션 컨테이너 */ | ||
const datePicker = document.getElementById('datePicker'); | ||
const addSectionBtn = document.getElementById('addSectionBtn'); | ||
const sectionInput = document.getElementById('sectionInput'); | ||
const todoSectionsDiv = document.getElementById('todoSections'); | ||
|
||
Comment on lines
+6
to
+10
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. 주로 getElementById를 사용하셨네요! 찾아보니 querySelector가 getElementById보다 느리다고 하네요.. 몰랐던 사실..!! 저는 주로 querySelector를 사용했는데, 지인님 덕분에 찾아보게 됐습니다 ㅎㅎ 저는 그냥 init 함수를 사용했었는데, 지인님처럼 DOMContentLoaded를 사용해서 dom이 완전히 로드된 다음 접근하는 게 더 좋은 거 같네요! |
||
// dom 로딩 완료 후, -> 날짜 선택-> 해당 날짜의 투두리스트 화면에 로드 | ||
datePicker.addEventListener('change', loadSectionsForDate); | ||
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. default값으로 dataPicker의 value 값을 당일 날짜로 해주면 사용하기에 더 좋을 것 같습니다~! |
||
// dom 로딩 완료 후, -> 섹션 추가되면, -> 섹션 추가 | ||
addSectionBtn.addEventListener('click', addTodoSection); | ||
|
||
// ** 함수 기능 : 로컬 스토리지에서 선택된 날의 전체 투두리스트 불러오기 -> html로 띄우기 | ||
function loadSectionsForDate() { | ||
const selectedDate = datePicker.value; | ||
todoSectionsDiv.innerHTML = ''; // 특정 날 투두리스트 불러올 준비 | ||
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 sections = getSectionsForDate(selectedDate); | ||
|
||
sections.forEach((section, 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. 자바스크립트 고차함수 사용까지 해주신 점 좋습니다~! |
||
// 모든 섹션을 화면에 보여주기위해 모든 존재하는 섹션에 대해 각각 createSectionDiv 호출 | ||
const sectionDiv = createSectionDiv(section, index); | ||
// 호출 결과(섹션별 준비된 구조) -> html에 추가 | ||
todoSectionsDiv.appendChild(sectionDiv); | ||
}); | ||
} | ||
|
||
// 투두 리스트 섹션 기능 1. 섹션 추가 / 2. 섹션 삭제 | ||
// 투두 리스트 섹션 추가 | ||
function addTodoSection() { | ||
const selectedDate = datePicker.value; | ||
const sectionName = sectionInput.value.trim(); | ||
if (!selectedDate || !sectionName) { | ||
alert('날짜를 선택하고 섹션 이름을 입력해주세요.'); | ||
return; | ||
} | ||
|
||
const sections = getSectionsForDate(selectedDate); | ||
sections.push({ name: sectionName, items: [] }); | ||
saveSectionsForDate(selectedDate, sections); | ||
// 로컬 스토리지에 추가한 섹션 -> loadSectionsForDate()로 html에 반영하기 | ||
loadSectionsForDate(); | ||
sectionInput.value = ''; | ||
} | ||
|
||
// 투두 리스트 섹션 삭제 | ||
function deleteSection(sectionIndex) { | ||
const selectedDate = datePicker.value; | ||
let sections = getSectionsForDate(selectedDate); | ||
sections.splice(sectionIndex, 1); | ||
// 로컬 스토리지에서 삭제한 섹션 -> loadSectionsForDate()로 html에 반영하기 | ||
saveSectionsForDate(selectedDate, sections); | ||
loadSectionsForDate(); | ||
} | ||
|
||
// 투두 리스트 아이템 기능 1. 아이템 추가 / 2. 아이템 삭제 | ||
function addTodoItem(sectionIndex, itemText) { | ||
if (!itemText.trim()) return; | ||
const selectedDate = datePicker.value; | ||
const sections = getSectionsForDate(selectedDate); | ||
sections[sectionIndex].items.push(itemText); | ||
saveSectionsForDate(selectedDate, sections); | ||
loadSectionsForDate(); | ||
} | ||
|
||
function deleteTodoItem(sectionIndex, itemIndex) { | ||
const selectedDate = datePicker.value; | ||
let sections = getSectionsForDate(selectedDate); | ||
sections[sectionIndex].items.splice(itemIndex, 1); | ||
saveSectionsForDate(selectedDate, sections); | ||
loadSectionsForDate(); | ||
} | ||
|
||
// createSectionDiv : html에 특정 섹션 표시하기 위해, 필요한 모든 요소 구조 준비 | ||
|
||
function createSectionDiv(section, index) { | ||
const sectionDiv = document.createElement('div'); | ||
sectionDiv.className = 'todo-section'; | ||
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. 주로 className 속성으로 접근하셔서 스타일링을 해주셨는데, 이 방법을 사용하면 class 속성 값 전체가 덮어씌워져 버려서 여러 가지 class를 적용하려면 classList.add 메소드를 사용하는 게 좋다고 하네요~ |
||
|
||
const sectionTitle = document.createElement('h3'); | ||
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. 알아보기 쉽게 코드에 주석 작성해주신 점 좋습니다~! |
||
updateSectionTitle(sectionTitle, section, index); | ||
|
||
const deleteSectionBtn = document.createElement('button'); | ||
deleteSectionBtn.textContent = '섹션 삭제'; | ||
deleteSectionBtn.onclick = () => deleteSection(index); | ||
sectionTitle.appendChild(deleteSectionBtn); | ||
|
||
sectionDiv.appendChild(sectionTitle); | ||
|
||
// 선택된 섹션에 할 일 아이템들을 먼저 추가 | ||
section.items.forEach((item, itemIndex) => { | ||
const itemDiv = document.createElement('div'); | ||
itemDiv.className = 'todo-item'; | ||
itemDiv.textContent = item; | ||
|
||
const deleteItemBtn = document.createElement('button'); | ||
deleteItemBtn.textContent = '완료'; | ||
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. 할 일 아이템별로 완료 버튼 말고도 삭제 버튼으로 삭제할 수 있는 기능이 있으면 좋을 것 같아요! |
||
deleteItemBtn.onclick = () => deleteTodoItem(index, itemIndex); | ||
itemDiv.appendChild(deleteItemBtn); | ||
|
||
sectionDiv.appendChild(itemDiv); | ||
|
||
return sectionDiv; | ||
}); | ||
|
||
// 현재 할 일 아이템들 아래에 할 일 추가를 위한 입력 필드와 버튼 추가 | ||
const itemInput = document.createElement('input'); | ||
itemInput.type = 'text'; | ||
itemInput.placeholder = '할 일 추가'; | ||
sectionDiv.appendChild(itemInput); | ||
|
||
const addItemBtn = document.createElement('button'); | ||
addItemBtn.textContent = '+'; | ||
addItemBtn.onclick = () => addTodoItem(index, itemInput.value); | ||
sectionDiv.appendChild(addItemBtn); | ||
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. append 메소드를 이용하면 여러 노드를 한번에 추가할 수 있어 더 간결하게 작성할 수 있어요! 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. 사용자가 입력 필드에서 엔터키를 눌러도 할 일이 추가되도록 itemInput에 이벤트 리스너를 추가해도 좋을 것 같아요! |
||
|
||
/* 할일 아이템 요소들 다 추가된 만들어진 섹션 요소 반환해서 | ||
섹션을 loadSectionsForDate()에서 forEach()로 화면에 표시할 수 있도록 함 | ||
*/ | ||
return sectionDiv; | ||
} | ||
|
||
// 로컬 스토리지에서 특정 날짜 투두 리스트 가져오기 | ||
function getSectionsForDate(date) { | ||
const sectionsJSON = localStorage.getItem(date); | ||
return sectionsJSON ? JSON.parse(sectionsJSON) : []; | ||
} | ||
|
||
// 로컬 스토리지에 특정 날짜 투두 리스트 업데이트하기 | ||
function saveSectionsForDate(date, sections) { | ||
localStorage.setItem(date, JSON.stringify(sections)); | ||
} | ||
}); | ||
|
||
// 남은 할 일의 수 섹션 타이틀에 반영해주는 함수 | ||
function updateSectionTitle(sectionTitle, section, index) { | ||
const remainingCount = countSectionItems(section); // 남은 할 일 수 계산 | ||
sectionTitle.textContent = `${section.name} (남은 할 일: ${remainingCount})`; | ||
} | ||
|
||
function countSectionItems(section) { | ||
// 완료되지 않은 할 일의 수를 계산 | ||
return section.items.filter((item) => !item.completed).length; | ||
} |
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.
이렇게 적어주시면 중복된 코드 줄일 수 있을 거 같아요! 그리고 보통 box-sizing: border-box; 같은 경우도, global.css로 따로 빼서 설정해주니, * 안으로 들어가면 좋을 거 같네요~ 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. reset.css나 global.css라는 파일을 둬서 브라우저의 stylesheet를 초기화하고 공통적으로 적용할 css를 정의해준다면 좋을 거 같습니다! |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,89 @@ | ||
/* 본인의 디자인 감각을 최대한 발휘해주세요! */ | ||
@font-face { | ||
font-family: 'SejonghospitalBold'; | ||
src: url('https://cdn.jsdelivr.net/gh/projectnoonnu/[email protected]/SejonghospitalBold.woff2') | ||
format('woff2'); | ||
} | ||
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: 100%; | ||
height: 100%; | ||
margin: 0; | ||
padding: 0; | ||
font-family: SejonghospitalBold, sans-serif; | ||
} | ||
|
||
html { | ||
width: 100%; | ||
height: 95%; | ||
margin: 0; | ||
padding: 0; | ||
font-family: SejonghospitalBold, sans-serif; | ||
} | ||
|
||
#app { | ||
display: flex; | ||
flex-direction: column; | ||
justify-content: flex-start; | ||
align-content: space-around; | ||
width: 80%; | ||
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. %를 활용해 좀 더 반응형이 되도록 신경써주신 점도 좋습니다~! |
||
height: 98%; | ||
|
||
margin: 30px auto; | ||
|
||
padding: 20px auto; | ||
overflow-y: auto; | ||
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. Overflow 되는 경우까지 꼼꼼히 처리해주셨네용👍 |
||
} | ||
|
||
.calendar-section, | ||
.todo-section, | ||
.add-item { | ||
width: 100%; /* 컨테이너 내부 너비 */ | ||
box-sizing: border-box; /* 패딩과 보더가 너비에 포함되도록 설정 */ | ||
} | ||
|
||
.todo-section { | ||
margin-bottom: 20px 10px; | ||
border: 1px solid #ccc; | ||
padding: 10px; | ||
border-radius: 5px; | ||
} | ||
|
||
input[type='text'], | ||
input[type='date'], | ||
button { | ||
font-family: SejonghospitalBold, sans-serif; | ||
padding: 8px; | ||
margin: 5px 0; | ||
border-radius: 5px; | ||
border: 1px solid #ccc; | ||
} | ||
|
||
button { | ||
background-color: #007bff; | ||
color: white; | ||
cursor: pointer; | ||
} | ||
|
||
button:hover { | ||
background-color: #0056b3; | ||
} | ||
|
||
.todo-section h3 { | ||
display: flex; | ||
justify-content: space-between; | ||
flex-wrap: wrap; | ||
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. 👍👍👍 |
||
align-content: space-around; | ||
font-size: 13px; | ||
} | ||
.todo-item { | ||
display: flex; | ||
justify-content: space-between; | ||
flex-wrap: wrap; | ||
align-content: space-around; | ||
font-size: 10px; | ||
} | ||
|
||
#todoSections { | ||
max-height: 98vh; | ||
overflow-y: auto; | ||
} |
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.
캘린더까지 구현해주신 점 정말 좋습니다!