---
# System prepended metadata

title: 認識瀏覽器的儲存空間-localStorage

---

# 認識瀏覽器的儲存空間-localStorage

![](https://i.imgur.com/t8vbS3y.jpg)


### :gem:何謂localStorage

localStorage是由html5所提供的一個web storage，有下列幾項特點:

* 無法[跨域](https://developer.mozilla.org/zh-TW/docs/Web/HTTP/CORS)使用，與cookie一樣只認domain name
* 達到5MB的儲存數據空間可以使用來降低request數量
* 用來儲存一些不重要但卻影響著效能等等的資訊。



---



### :gem:關於localStorage、sessionStorage、cookie 差異

| 項目             | localStorage                                 | sessionStorage       | Cookie                               |
| ---------------- | -------------------------------------------- | -------------------- | ------------------------------------ |
| 儲存數據         | 可以達到 5M 或更大                           | 可以達到 5M 或更大   | 不能超過4k                           |
| 生命週期         | 瀏覽器關閉後資料不會移除，除非用戶端清除資料 | 刷新頁面資料依舊存在，關閉瀏覽器後資料移除 | 在後端設置的cookie過期時間內都會有效 |
| 傳遞資料到Server | 否                                           | 否                   | 是                                   |




---

### :pencil:localStorage 簡單練習
#### <font color=#FF6600>**PART1**</font>

最近在工作實作上遇到下面的情境:speech_balloon:
(待補)

想到的兩種解法是
1.判斷是否打過API 
2.使用localStorage儲存數據


最後我選擇用localStorage實作

透過這兩種方式來記住使用者是否曾經點擊過此按鈕，
若已點擊過A按鈕，就算重整頁面B按鈕也會是取消禁用的狀態



#### :bulb:知識點
**localStorage**
1. `localStorage.setItem('key','value')`:透過setItem指定物件屬性的`key`跟`value`
2. `locaStorage.getItem('key')`:透過getItem方法屬性中`key`，可以得到屬性中對應的`value`

**Event**
1. [target.addEventListener](https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/addEventListener)

> html

```
<div>
  <ul>
    <li style="margin-left:45px; list-style-type:none">
      <button type="button"  
      class="download">A</button>
    </li>

    <li style="margin-left:45px; list-style-type:none">
      <button type="button"
      class="download" disabled="disable">B</button>
    </li>
  </ul>
</div>
```

> javascript
```
let csrBtn = document.querySelector(".download");

function saveBtnClick() {
  localStorage.setItem("setValue", 1); //儲存到localStorage 記錄使用者是否點擊過
}
csrBtn.addEventListener("click", saveBtnClick);

let str = localStorage.getItem("setValue"); //透過屬性的中的key，得到對應的value
if (str == 1) {
  $("#touchBtn").removeAttr("disabled");
}

```


#### <font color=#FF6600>**PART2**</font>

[JavaScript30-NO.15 ](https://github.com/soyaine/JavaScript30)

透過localStorage的作法做資料的新增刪除。

#### :bulb:知識點
**Event**
1. event.preventDefault
2. [event.addEventListener](https://developer.mozilla.org/zh-CN/docs/Web/API/EventTarget/addEventListener)

**localStorage**

1. `localStorage.setItem('key','value')`
2. `locaStorage.getItem('key')`

**JSON**
1. `JSON.stringify()`
2. `JSON.parse()`

**作法:**

:one:取得form元素，並宣告一個空陣列準備放入資料。
> javascript
```
const addItems = document.querySelector('.add-items');
const itemsList = document.querySelector('.plates');
var items =  []; 
```

:two:寫一個addItem函式，選取form中輸入框欄位值

```
function addItem(e) {
      e.preventDefault(); //避免每新增一筆資料時就重整一次頁面
      const text = this.querySelector('[name=item]').value;
      const item = {
          text: text, //要放入的清單
          done: false //是否勾選
      }
      items.push(item);
      this.reset();
      .....
  }
```

:three:顯示新增清單


```
function populateList(plates = [], plateslist) { //新增列表
    plateslist.innerHTML = plates.map((plate, i) => { 
    return `
    <li>
	<input type="checkbox" data-index=${i} 
    id="item${i}" ${plate.done ? 'checked' : ''} >
                <label for="item${i}">${plate.text}</label>
            </li>
          `;
      }).join('');
  }
```
* [ES6寫法在函式中給予參數的地方放入預設值](https://pjchender.blogspot.com/2017/01/es6-default-value.html)
* 使用map將所有陣列中的元素依序分別傳入一次至 callback 函式當中，並以此回呼函式每一次被呼叫的回傳值來建構一個新的陣列
* 使用`join`將陣列所有元素連結，合併成一個字串。
 

:four: 再回到addItem函式把[key,value]寫入localStorage


```
function addItem(e) {
      .....
      populateList(items, itemsList); //輸入送出後重新列出物件字串
	  localStorage.setItem('items', JSON.stringify(items));
     .....
  }
```
將items(自定義)存入localStorage中，因localStorage值是字串，需透過JSON.stringify將
物件或陣列轉為字串。

若沒轉為字串，新增資料時會得到下面的結果
![](https://i.imgur.com/mSv3I5m.png)

:five:儲存checkbox狀態

 ```
function toggleDone(e) {    //check
	  if (!e.target.matches('input')) return; //點擊的位置是否是input
	  const el = e.target;
	  const index = el.dataset.index; //取得checkbox位置
	  items[index].done = !items[index].done; //checkbox true or false
      
	  populateList(items, itemsList); //更新數據
	  localStorage.setItem('items', JSON.stringify(items)); //更新後的狀態寫入localStorage
  }
  
  addItems.addEventListener('submit', addItem);
  itemsList.addEventListener('click', toggleDone);
  populateList(items, itemsList);
```

:six: 新增刪除、選取所有checkbox、取消所有checkbox

 ```
const checkAllBtn = document.querySelector('.check-all');
const unCheckAllBtn = document.querySelector('.uncheck-all');
const deleteAllBtn = document.querySelector('.delete-all');

```

```
checkAllBtn.addEventListener('click', () => {
//透過迴圈遍歷每個item將checkbox狀態改為勾選
  	items.forEach(item => {
  		item.done = true;	  
  	});
    populateList(items, itemsList); //更新數據
    localStorage.setItem('items', JSON.stringify(items));
  });  
	
unCheckAllBtn.addEventListener('click', () => {
  //遍歷迴圈的每個item將checkbox狀態改為取消勾選
  	items.forEach(item => {
  		item.done = false;
  	});
  	populateList(items, itemsList);//更新數據
    localStorage.setItem('items', JSON.stringify(items));
  });  
	
deleteAllBtn.addEventListener('click', () => {
  	items = []; //將一開始宣告的陣列設為空值
  	populateList(items, itemsList);//更新數據
   	localStorage.setItem('items', JSON.stringify(items));
  });
```

DEMO:[https://evalin0316.github.io/localStorage/localStorage.html](https://evalin0316.github.io/localStorage/localStorage.html)


> 參考資料:
>* http://techaride.blogspot.com/2012/07/javascript-localstorage.html
>* https://github.com/FEGuideTeam/FEGuide/tree/master/html%E9%97%AE%E9%A2%98


