Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Frontend Mentor - Tip calculator app

- [x] html, css 레이아웃
- [x] 반응형 디자인
- [x] 입력창
- [x] 결과창
- [ ] tip 계산

![Design preview for the Tip calculator app coding challenge](./design/desktop-preview.jpg)

## Welcome! 👋
Expand Down
135 changes: 93 additions & 42 deletions index.html
Original file line number Diff line number Diff line change
@@ -1,44 +1,95 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- displays site properly based on user's device -->

<link rel="icon" type="image/png" sizes="32x32" href="./images/favicon-32x32.png">

<title>Frontend Mentor | Tip calculator app</title>

<!-- Feel free to remove these styles or customise in your own stylesheet 👍 -->
<style>
.attribution { font-size: 11px; text-align: center; }
.attribution a { color: hsl(228, 45%, 44%); }
</style>
</head>
<body>

Bill

Select Tip %
5%
10%
15%
25%
50%
Custom

Number of People

Tip Amount
/ person

Total
/ person

Reset

<div class="attribution">
Challenge by <a href="https://www.frontendmentor.io?ref=challenge" target="_blank">Frontend Mentor</a>.
Coded by <a href="#">Your Name Here</a>.
</div>
</body>
</html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- displays site properly based on user's device -->

<link
rel="icon"
type="image/png"
sizes="32x32"
href="./images/favicon-32x32.png"
/>
<link rel="stylesheet" href="style.css" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Kumbh+Sans:wght@400;700&display=swap"
rel="stylesheet"
/>

<title>Frontend Mentor | Tip calculator app</title>
</head>
<body>
<div class="container">
<div class="logo-section">
<img src="images/logo.svg" />
</div>
<div class="calculator-section">
<div class="input-section">
<div class="input-title-box">
<div class="input-title">Bill</div>
<div class="input-alert bill-alert">Can't be zero</div>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

저는 사람수가 0이 되는 경우에 대해서만 alert 처리를 했는데, input에 대해서도 0일 때를 처리하니 더 디테일이 사는 것 같아요!

</div>

<input
type="text"
class="calculator-input bill-input"
placeholder="0"
oninput="this.value = this.value.replace(/[^0-9.]/g, '').replace(/(\..*)\./g, '$1');"
/>
Comment on lines +36 to +41

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

이건 뭔가요??
저... 신기해요... oninput에 replace 2번에...저 이 코드 해석이 안되요....

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

숫자만 입력하고 싶은데 type="number"로 할 때, 옆에 버튼이 생기는 게 싫어서 입력 정규식을 사용했습니다. 그런데... 저도 검색해서 사용했던 거라 자세히 보지 않았는데... 이 정규식은 소수 하나만 허용하는 정규식이라고 하네요... 미스테이크...


<div class="input-title">Select Tip %</div>
<div class="select-tip-section">
<button class="select-tip-button" value="0.05">5%</button>
<button class="select-tip-button" value="0.10">10%</button>
<button class="select-tip-button" value="0.15">15%</button>
<button class="select-tip-button" value="0.25">25%</button>
<button class="select-tip-button" value="0.50">50%</button>
<input
type="text"
class="custom-tip-input"
placeholder="Custom"
oninput="this.value = this.value.replace(/[^0-9.]/g, '').replace(/(\..*)\./g, '$1');"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

oninput에서 사용되는 이벤트 핸들링 로직이 중복되므로 재사용성을 위해 함수로 분리 해도 좋을 것 같다고 생각합니다!

/>
</div>

<div class="input-title-box">
<div class="input-title">Number of People</div>
<div class="input-alert people-alert">Can't be zero</div>
</div>

<input
type="text"
class="calculator-input people-input"
placeholder="0"
oninput="this.value = this.value.replace(/[^0-9.]/g, '').replace(/(\..*)\./g, '$1');"
/>
</div>
<div class="result-section">
<div class="tip-result-section">
<div class="tip-result-row">
<div class="result-title-box">
<div class="result-title">Tip Amount</div>
<div class="result-subtitle">/ person</div>
</div>
<div class="tip-result tip-per-person">$ 0.00</div>
</div>

<div class="tip-result-row">
<div class="result-title-box">
<div class="result-title">Total</div>
<div class="result-subtitle">/ person</div>
</div>
<div class="tip-result total-per-person">$ 0.00</div>
</div>
</div>

<button class="reset-button">RESET</button>
</div>
</div>
</div>
</body>
<script src="index.js"></script>
</html>
108 changes: 108 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
const billInput = document.querySelector(".bill-input");
const billAlert = document.querySelector(".bill-alert");

const tipRatioInputs = document.querySelectorAll(".select-tip-button");
const customTipRatioInput = document.querySelector(".custom-tip-input");

const peopleInput = document.querySelector(".people-input");
const peopleAlert = document.querySelector(".people-alert");

const tipPerPersonOutput = document.querySelector(".tip-per-person");
const totalPerPersonOutput = document.querySelector(".total-per-person");
const resetButton = document.querySelector(".reset-button");
Comment on lines +1 to +12

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

의미에 맞게 나누어서 띄어쓰기를 한 것이 보기 좋은 것 같습니다.


let bill = 0;
let tipRatio = 0;
let people = 0;
let tipPerPerson = 0;
let totalPerPerson = 0;
Comment on lines +14 to +18

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

변순 직관적이라서 좋지만, 관련 있는 데이터는 묶어서 데이터를 관리하는 것이 좋습니다.


const calculate = () => {
if (bill === 0 || tipRatio === 0 || people === 0) {
return;
}

tipPerPerson = (bill * tipRatio) / people;
totalPerPerson = bill / people + tipPerPerson;

tipPerPersonOutput.innerHTML = `$ ${tipPerPerson.toFixed(2)}`;
totalPerPersonOutput.innerHTML = `$ ${totalPerPerson.toFixed(2)}`;
};

const onBillChange = (e) => {
if (e.target.value === "0") {
billAlert.style.visibility = "visible";
e.target.style.border = "1px solid red";
} else {
billAlert.style.visibility = "hidden";
e.target.style.border = "none";

bill = Number(e.target.value);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

단항 연산자 + 를 사용하면 명시적으로 숫자형으로 변환할 수 있습니다.
한번 사용해보시는 것도 좋을 것 같아요!

Suggested change
bill = Number(e.target.value);
bill = +e.target.value;

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

사실 당시에 parseInt()가 생각나지 않아서 썼던 코드인데 이 방법이 더 간편하네요!

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

오 저도 알아갑니다!!

calculate();
}
};

const onTipRatioClick = (e) => {
tipRatioInputs.forEach((tipRatioInput) => {
tipRatioInput.classList.remove("selected");
});
customTipRatioInput.value = null;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

value에 null보다는 빈 문자열은 어떨까요 ?

자바스크립트에서는 이미 값이 없음을 나타내는 undefined 라는 타입이 있으니, 같은 개념을 가진 null을 사용할 필요가 없다고 합니다. (또한 input tag의 value 초기값은 빈 문자열이라 생각합니다.)

Why I banned ‘null’ from my Javascript code and why you should too

Don’t repeat yourself. Javascript already has the undefined type to indicate the absence of value, so there is absolutely no need to have a second type that represent the same concept.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

스터디 때 다뤘던 내용!!!인데 순간 거꾸로 착각하고 코드를 작성했네요. 다음부터는 조금 차분하게 스프린트를 진행해야겠습니다.... 그나저나 input tag의 value 초기값이 빈 문자열인 건 이번에 처음 알았어요!


e.target.classList.add("selected");
tipRatio = Number(e.target.value);
calculate();
};

const onCustomTipRatioChange = (e) => {
tipRatioInputs.forEach((tipRatioInput) => {
tipRatioInput.classList.remove("selected");
});
Comment on lines +57 to +59

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

자주 쓰이는 로직이므로 분리해서 사용하는 건 어떨까요?


tipRatio = Number(e.target.value);
calculate();
};

const onPeopleChange = (e) => {
if (e.target.value === "0") {
peopleAlert.style.visibility = "visible";
e.target.style.border = "1px solid red";
} else {
peopleAlert.style.visibility = "hidden";
e.target.style.border = "none";

people = Number(e.target.value);
calculate();
}
};

const onResetButtonClick = (e) => {
bill = 0;
tipRatio = 0;
people = 0;
tipPerPerson = 0;
totalPerPerson = 0;

billInput.value = null;
billInput.style.border = "none";
billAlert.style.visibility = "hidden";

tipRatioInputs.forEach((tipRatioInput) => {
tipRatioInput.classList.remove("selected");
});
customTipRatioInput.value = null;

peopleInput.value = null;
peopleInput.style.border = "none";
peopleAlert.style.visibility = "hidden";

tipPerPersonOutput.innerHTML = "$ 0.00";
totalPerPersonOutput.innerHTML = "$ 0.00";
};

billInput.addEventListener("keyup", onBillChange);
tipRatioInputs.forEach((tipRatioInput) => {
tipRatioInput.addEventListener("click", onTipRatioClick);
});
customTipRatioInput.addEventListener("keyup", onCustomTipRatioChange);
peopleInput.addEventListener("keyup", onPeopleChange);
resetButton.addEventListener("click", onResetButtonClick);
Loading