---
# System prepended metadata

title: JS練習_記帳本
tags: [javascript]

---

# JS練習_記帳本
製作一個記帳簿，可以增加收入及支出，並呈現加總金額。

-  成品畫面：

![](https://i.imgur.com/YcE8kKA.png)


- [成果網頁](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讓他變紅色
![](https://i.imgur.com/ORy4byy.png)

- [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`