# JS練習_記帳本
製作一個記帳簿,可以增加收入及支出,並呈現加總金額。
- 成品畫面:

- [成果網頁](https://chrislinlin.github.io/my-projects/mentor_mission/mission_24/index.html)
## 功能
- [x] 每個品項記載名稱及金額,並依照金額給予特定屬性。
- [x] 單一頁面實現新增、刪除品項及各項加總。
## html
- 中間的HISTORY是用容器包覆著`ul`,每筆新增的項目會表示在`ul`裡及`form`
- 最下面則是用`form`讓使用者填寫新增的項目。
## css
- 使用變數儲存陰影的設定,可以迅速且方便的取用。
```.css
:root{
--box-shadow: 0 1px 3px rgba(0,0,0,.12),
0 1px 2px rgba(0,0,0.24);
/*取用時以box-shadow: var(--box-shadow)即可取用*/
}
```
- 在選擇器上,使用`<tag>:first-of-type`指的是某父元素下相同型別子元素中的第一個,像是本次`div:first-of-type` 就是在某父型別下的第一個div。
- [first-type-of 小科普](https://developer.mozilla.org/en-US/docs/Web/CSS/:first-of-type)
- 在容器右邊設定邊線,達成中間有分隔線的效果。
```.css
.inc-exp-container >div:first-of-type{
border-right: 1px solid #dedede
}
```
- 在父元素容器,進行切分,利用flex設定為一,使子元素自動平分空間
```.css
.inc-exp-container >div{
flex: 1;
text-align: center;
}
```
## JavaScript
### 步驟
- 輸入要新增的內容呈現在上面列表
- 上面列表內容金額自動加總
- 金額正數呈現綠色,金額負數呈現紅色
- 點選刪除鍵,總金額自動計算,列表內容刪除
### addItem()
使輸入的項目,新增至上方HISTORY,同時也新增一個刪除按鈕。
- 將原先`li`標籤設定minus屬性,並設定參數條件,若符合條件,則移除minus屬性並新增plus屬性。
- 當選擇在history刪除項目時,點選==x==觸發事件,記得要加上`last()`,不然會把所有輸入的項目都刪掉,不會只刪一個。
- [last 小科普](https://api.jquery.com/last/)
- append()/ appendTo()
兩種方法功能相同,都是將目標加入容器之後但語法不同。
- (表達式).append(參數)
- (加入物(參數)).appendTo(容器(表達式))
```.js=
//add item in history
function addItem(name, amount, id ,transactions){
var item_str = '<li class =minus>'+name+'<span>'+amount+'</span>'+'<button class = "delete-btn" data-id="'+id+'">x</button>';
$('#list').append(item_str)
if(amount>0){
$('li').toggleClass('minus')
$('li').addClass('plus')
}
clearForm();
$('.delete-btn').last().click(function(){
$(this).parent().remove();
var id =$(this).data('id');
deleteItemFormLocalstorage(transactions, id);
updateValues();
})
}
```
- [append to 小科普](https://api.jquery.com/appendto/)
### localstorage
建立一個變數`transactions`,將儲存在localstorage的資料拉出來,並利用`JSON parse()`將拉出來的資料轉換成陣列形式。
```.js
var transactions = JSON.parse(localStorage.getItem('myTransactions')) || [];
```
### generateID()
- 產生亂數代表決定我們要在列表新增多少項目。
- `Math.random()`:會產生0~1間的小數。
- `Math.floor()`:會將小數點捨去並回傳小於等於產生的亂數的最大整數。
- 乘上100000倍,代表產生亂數的數量。
```.js
function generateID(){
return Math.floor(Math.random()*1000000)
};
```
### clearform ()
每當新增項目時(要在addItem()呼叫它),會清除使用者輸入欄的值,讓使用者得進行下一次的輸入。
```.js
function clearForm(){
$('#form').find("input").val("");
}
```
### deleteItemFormLocalStorage()
設定條件,要是id完全符合時,使用 `splice()`方法,刪除陣列項目,
再利用使用`JSON.stringify`,將資料陣列轉為字串重新更新localstorage。
- [splice()小科普](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array/splice)
```.js
function deleteItemFormLocalstorage(transactions, id){
transactions.forEach(function(item, index, arr){
if(item.id === id){
arr.splice(index, 1);
}
});
localStorage.setItem('myTransactions', JSON.stringify(transactions));
}
```
```.js=
var transactions = JSON.parse(localStorage.getItem('myTransactions')) || [];
$(document).ready(function(){
if(transactions.length >0){
initHistory(transactions);
}
$("#form").find('button').click(function(e){
e.preventDefault();
var name = $('#text').val();
var amount = $('#number').val();
var id = generateID()
addItem(name, amount, id, transactions);
//push()讓陣列可以丟進內容
transactions.push({id: id, name: name, amount: amount});
localStorage.setItem('myTransactions', JSON.stringify(transactions));
updateValues();
})
})
```
### updateValue()
1. 處理陣列的金額,利用`map()`運算回傳新的陣列
2. 每一筆交易加總,使用`reduce()`方法,抓取每一項目的金額進行加總
- reduce()
```.js=
const sum = array.reduce((accumulator, element) => {
return accumulator + element;
}, 0);
```
- [reduce()小科普](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce)
3. 計算收入/ 支出/ 總數金額,使用`filter()`方法,設定函式計算,經過運算,回傳新的陣列。
- filter() 得到元素/ 物件集合中的符合的表達式,把要的物件/元素選出來
```.html
<ul>
<li>list item 1</li>
<li>list item 2</li>
<li>list item 3</li>
<li>list item 4</li>
<li>list item 5</li>
<li>list item 6</li>
</ul>
```
```.js=
$('li').filter(':even').css('background-color', 'red');
```
那選出來的元素會是索引號為偶數得物件,然後我們用css讓他變紅色

- [filter()小科普](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array/filter)
4. 加入內文至選定的元素
5. 在函式initHistory 及點擊事件時呼叫他,使事件發生時能更新資訊。
```.js
function updateValues() {
const amounts = transactions
.map(function(transaction) {
return transaction.amount;
})
// reduce()方法:累加陣列中數值
const total = parseFloat(amounts
.map(Number) //單純的amount是字串陣列
.reduce((function(accumulator, item_str) {
return accumulator += item_str; // accumulator = accumulator + item;
}),0))
.toFixed(2);
// console.log(total)
// filter()方法: 經過內部函式處理後,將通過之元素回傳為新陣列
// https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
const income = parseFloat(amounts
.filter(function(item_str) {
return item_str > 0;
})
.reduce((function(accumulator, item_str) {
return accumulator -= item_str;
}),0))*-1
.toFixed(2);
console.log(income)
const expense = parseFloat((amounts
.filter(function(item_str) {
return item_str < 0;
})
.reduce((function(accumulator, item_str) {
return accumulator -= item_str; //acc=acc-item
}),0) * -1))
.toFixed(2);
// console.log(expense)
$('#balance').text(`$${total}`);
$('#money-plus').text(`$${income}`);
$('#money-minus').text(`$${expense}`);
}
```
### initHistory()
用 `forEach()`方法跑一次物件的資料,等呼叫函式時,可以直接顯示。
```.js
function initHistory(transactions){
transactions.forEach(function(item){
addItem(item.name, item.amount, item.id, transactions)//item.id也要記得加
})
updateValues();
}
```
###### tags: `javascript`