Skip to content
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

[ 2주차 기본/심화/생각 과제 ] 웨비의 사진관과 가계부 #4

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Binary file added .DS_Store
Binary file not shown.
Binary file added Session02 /.DS_Store
Binary file not shown.
1 change: 1 addition & 0 deletions Session02 /.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.DS_Store
52 changes: 52 additions & 0 deletions Session02 /가계부과제/account.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="style.css" />
<title>2차세미나 가계부 과제</title>
</head>
<body>
<header><h1>민영이 가계부**</h1></header>
<section class="summary-article"></section>

<section class="manage">
<nav class="manage-date">
<button>〈</button>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요거 참고하면 좋을 것 같아! 나도 저번에 코드리뷰 받고 부등호 수정했다ㅎㅎ
HTML 특수문자 리스트

<p>10월 13일</p>
<button>〉</button>
Copy link

@urjimyu urjimyu Oct 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

버튼은 버튼 타입 명시해주기! 안 그러면 기본적으로 submit 타입으로 지정된다고 해요
button 타입 잊지 않기☺️
MDN - button

</nav>

<section class="manage-list">
<header>
<h3>내역 리스트</h3>
<form>
<input name="income" type="checkbox" id="income-ID" checked />
<label for="income-ID">수입</label>
<input name="outcome" type="checkbox" id="outcome-ID" checked />
<label for="outcome-ID">지출</label>
</form>
</header>
<ul></ul>
</section>
</section>

<footer>
<button type="button">+</button>
</footer>

<section class="list-delete-modal">
<section class="modal">
<p>정말 삭제하시겠습니까?</p>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

삭제 모달도 구현했네요! 최고👍🏻

<div>
<button type="button" class="delete_yes">YES</button>
<button type="button" class="delete_no">NO</button>
</div>
</section>
</section>

<section class="add_list"></section>

<script src="account.js"></script>
</body>
</html>
263 changes: 263 additions & 0 deletions Session02 /가계부과제/account.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
const INIT_BALANCE = 0;
const HISTORY_LIST = [
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

상수 파일을 따로 빼서 쓰면 더 가독성이 좋을 것 같아요!☺️

{
id: 0,
category: "식비",
description: "스타벅스",
price: -10000,
},
{
id: 1,
category: "서적",
description: "자구교재",
price: -500000,
},
{
id: 2,
category: "식비",
description: "스타벅스",
price: -5000,
},
{
id: 3,
category: "용돈",
description: "고모부",
price: +250000,
},
];

//summary-article
//2.2 총수입 총지출 반영
function summary_element() {
let plus = 0;
let minus = 0;
historyList.forEach((el) => {
el.price > 0 ? (plus += el.price) : (minus -= el.price);
});

const summary_element_H3 = document.createElement("h3");
summary_element_H3.textContent = "나의 자산";

const summary_element_current = document.createElement("p");
summary_element_current.textContent = (plus - minus).toLocaleString();

const summary_element_detail = document.createElement("div");
summary_element_detail.className = "detail";

//detail-수입
const plusBtn = document.createElement("i");
plusBtn.textContent = "+";
const plusSpan = document.createElement("span");
plusSpan.textContent = plus.toLocaleString();
plusSpan.className = "plus";
Comment on lines +48 to +52
Copy link

@urjimyu urjimyu Oct 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

클래스네임으로 이렇게 구현하는 방법도 있군요👍 다른 방법도 슬쩍 소개해보자면 상수 폴더에 지출인지 수입인지 분류한 다음 부호를 붙여주는 방법도 가능해요~!


//detail-지출
const minusBtn = document.createElement("i");
minusBtn.textContent = "-";
const minusSpan = document.createElement("span");
minusSpan.textContent = minus.toLocaleString();
minusSpan.className = "minus";

summary_element_detail.appendChild(plusBtn);
summary_element_detail.appendChild(plusSpan);
summary_element_detail.appendChild(minusBtn);
summary_element_detail.appendChild(minusSpan);

const summary_article_add_element = document.querySelectorAll(".summary-article")[0];
summary_article_add_element.innerHTML = ""; // 한번 초기화 후 다시 생성
summary_article_add_element.appendChild(summary_element_H3);
summary_article_add_element.append(summary_element_current);
summary_article_add_element.append(summary_element_detail);
}

const historyList = HISTORY_LIST;
// 2.1(b) 상수 데이터 렌더링
function list_element(id, category, description, price) {

const categorySpan = document.createElement("span");
categorySpan.textContent = category;
categorySpan.className = "category";

const descriptionSpan = document.createElement("span");
descriptionSpan.textContent = description;
descriptionSpan.className = "description";

const priceSpan = document.createElement("span");
priceSpan.textContent = (price > 0 ? "+" : "") + price.toLocaleString();
priceSpan.className = price < 0 ? "outcome" : "income";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

삼항 연산자 야무지게 썼군요! 좋아요✨


const deleteListBtn = document.createElement("i");
deleteListBtn.textContent = "delete";
deleteListBtn.className = `delete_list_Button ${id}`;

//2.4 delete 이벤트 : id로 delete 식별
deleteListBtn.addEventListener("click", function () {
handleDelete(id);
});

//2.1-(a,b)
const li = document.createElement("li");
li.className = price < 0 ? "income-list" : "outcome-list";
li.appendChild(categorySpan);
li.appendChild(descriptionSpan);
li.appendChild(priceSpan);
li.appendChild(deleteListBtn);

const ul = document.querySelector("ul");
ul.appendChild(li);
}

//2.3 수입 지출 필터링
let plusChecked = true;
let minusChecked = true;

function handleFilter() {
const incomeList = document.querySelectorAll(".outcome-list");
const outcomeList = document.querySelectorAll(".income-list");
incomeList.forEach((list) => {
list.style.display = plusChecked ? "flex" : "none";
});
outcomeList.forEach((list) => {
list.style.display = minusChecked ? "flex" : "none";
});
}
const incomeFilter = document.querySelector('label[for="income-ID"]');
incomeFilter.addEventListener("click", function () {
minusChecked = !minusChecked;
handleFilter();
});
const outcomeFilter = document.querySelector('label[for="outcome-ID"]');
outcomeFilter.addEventListener("click", function () {
plusChecked = !plusChecked;
handleFilter();
});

//2.4 식별된 id로 delete
function handleDelete(id) {
const listDeleteModal = document.querySelectorAll(".list-delete-modal")[0];
//심화 2.1(a,b) 삭제 모달 추가
listDeleteModal.style.display = "flex"; // 모달 띄우기
const deleteYesBtn = document.querySelectorAll(".delete_yes")[0];
const deleteNoBtn = document.querySelectorAll(".delete_no")[0];
//yes no 클릭시
deleteYesBtn.onclick = function () {
historyList.forEach((el, idx) => {
el.id === id && historyList.splice(idx, 1);
});
listDeleteModal.style.display = "none";
render();
};
deleteNoBtn.onclick = function () {
listDeleteModal.style.display = "none";
}; //팝업 형식
}

//2.5 리스트 추가
function handleAddFilter(idx) {
const filter_button = document.querySelectorAll(".add_filter_btn>button");
const selectTag = document.querySelector("#category");
Comment on lines +157 to +158
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

querySelect 같이 JS에서 자주 쓰이는 메서드는 상수로 선언해서 좀 더 편하게 사용 가능합니다. 그냥 참고!

const $ = (selector) => document.querySelector(selector);

selectTag.innerHTML = "";

if (idx === 0) {
// 수입
filter_button[0].className = "selected-button";
filter_button[1].className = "unselected-button";

const tags = ["용돈", "포인트"];
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

가계부 아이템 나중에 상수로 뺄 때 수입/지출도 프로퍼티로 추가하면 그 아이템만 가져오도록 수정할 수 있을 거예요!

tags.forEach((el) => {
const optionTag = document.createElement("option");
optionTag.value = el;
optionTag.textContent = el;
selectTag.appendChild(optionTag);
});
} else {
filter_button[1].className = "selected-button";
filter_button[0].className = "unselected-button";

const tags = ["식비", "서적"];
tags.forEach((el) => {
const optionTag = document.createElement("option");
optionTag.value = el;
optionTag.textContent = el;
selectTag.appendChild(optionTag);
});
}
}

//2.5-(2,3) footer -> 카테고리 선택 및 금액과 내역 입력
function handleAddSheet() {
let selected_idx = 0; //0:수입 1:지출
const addList = document.querySelectorAll(".add_list")[0];
addList.innerHTML = `
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

innerHTML을 지양하는 것이 좋습니다! 아래 참고 자료에 설명이 잘 되어 있어서 첨부합니다.
요약하자면 느리고 공격에 취약해 Node.textContent or Element.insertAdjacentHTML() 사용을 추천합니다!
innerHTML을 지양해야 하는 이유

<section class="add_list_content">
<h2>내역 추가</h2>
<div class="add_filter_btn">
<button type="button" class="selected-button">수입</button>
<button type="button" class="unselected-button">지출</button>
</div>

<form>
<label for="category">종류</label>
<select id="category">
<option value="용돈">용돈</option>
<option value="포인트">포인트</option>
</select>
<label for="add_price">금액</label>
<input type="number" id="add_price" />
<label for="add_data">내용</label>
<input type="text" id="add_data" />
</form>

<button type="button" class="add_save_btn">저장하기</button>
<button type="button" class="add_close_btn">닫기</button>
</section>
`;
addList.style.display = "flex";

const filter_button = document.querySelectorAll(".add_filter_btn>button");
filter_button.forEach((btn, idx) => {
btn.addEventListener("click", function () {
selected_idx = idx;
handleAddFilter(selected_idx);
});
});
//2.5-(4,5) footer 모달 저장하기 닫기
const addSaveBtn = document.querySelectorAll(".add_save_btn")[0];
const addCloseBtn = document.querySelectorAll(".add_close_btn")[0];
addSaveBtn.addEventListener("click", function () {
const select = document.querySelector("#category");
const option = select.options[select.selectedIndex].value;
let price = document.querySelector("#add_price").value;
const data = document.querySelector("#add_data").value;
price = selected_idx === 1 ? -price : +price; //지출일 경우, 가격 음수처리

historyList.push({
id: historyList.length,
category: option,
description: data,
price: price,
});

render();
alert("저장성공>,<");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ㅋㅋ귀여워!!

});
addCloseBtn.addEventListener("click", function () {
addList.style.display = "none";//팝업 형식
});

}
const footerBtn = document.querySelector("footer>button");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

야무진 선택자 활용!🙌🏻

footerBtn.addEventListener("click", handleAddSheet);

// 실제 렌더링
function render() {
summary_element();
document.querySelector("ul").innerHTML = ""; // 리스트 초기화

historyList.forEach((el) => {
list_element(el.id, el.category, el.description, el.price);
});
}

// 실제 렌더링
render();
48 changes: 48 additions & 0 deletions Session02 /가계부과제/listExample.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<li>
<span class="list-category">식비</span>
<span class="list-description">스타벅스</span>
<span class="list-amount-minus">- 10,000원</span>
<button type="button">X</button>
</li>
<li>
<span class="list-category">용돈</span>
<span class="list-description">고모부</span>
<span class="list-amount-plus">+ 250,000원</span>
<button type="button">X</button>
</li>
<li>
<span class="list-category">식비</span>
<span class="list-description">스타벅스</span>
<span class="list-amount-minus">- 5,000원</span>
<button type="button">X</button>
</li>
<li>
<span class="list-category">서적</span>
<span class="lis-description">자구교재</span>
<span class="list-amount-minus">- 50,000원</span>
<button type="button">X</button>
</li>
<li>
<span class="list-category">식비</span>
<span class="list-description">냉우동</span>
<span class="list-amount-minus">- 18,000원</span>
<button type="button">X</button>
</li>
<li>
<span class="list-category">쇼핑</span>
<span class="list-description">머리띠</span>
<span class="list-amount-minus">- 4,900원</span>
<button type="button">X</button>
</li>
<li>
<span class="list-category">식비</span>
<span class="list-description">초코칩</span>
<span class="list-amount-minus">- 1,500원</span>
<button type="button">X</button>
</li>
<li>
<span class="list-category">학용품</span>
<span class="list-description">화이트</span>
<span class="list-amount-minus">- 1,200원</span>
<button type="button">X</button>
</li>
Loading