iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 15
0

成品連結:LocalStorage操作前程式碼完成後程式碼

如標題揭示,今天的主題是 localStorage 以及如何使用它。

使用 localStorage 主要的目的是當瀏覽器重新整理時原畫面的資料依舊能夠存在。舉例來說,今天我們做了 to-do list,但不希望關閉視窗後待辦清單消失,這時候就是使用 localStorage 的絕佳時機了!

這天開始會附上操作前的程式碼,提供給想要一起練習的朋友,之前幾天未附上抱歉了...

從操作前程式碼可以看到已經預先宣告了幾個變數 addItemsitemsList 以及一個要用來存放新增項目但目前沒有內容的 array

新增清單項目

一開始先從新增項目開始,這裡目標是當按下 + Add Item 按鈕時將輸入內容加入至清單

設定監聽事件

首先先設定監聽事件。從 addItems 可以看到綁定元素是表單本身而不是按鈕而已;這裡用的事件是 submit

addItems.addEventListener('submit', addItem);

也就是當按下 + Add Item,表單會自動提交

將內容新增至 array items

這裡會發現當按下新增項目時瀏覽器會重整,如果你試著在 addItem 內印出東西會一瞬間跳掉;這是因為 submit 預設動作會提交表單並重新整理,如果要畀秒這個情況,要寫入 e.preventDefault()

function addItem(e) {
    e.preventDefault();
}

接著要把使用者輸入的資料輸入 items 當中,這裡使用一個新的 object 儲存資料,而 object 中除了要記錄輸入內容,還要記錄是否被選取(也就是被加到清單內後是否被打勾),預設是 false,並把資料推入 items

function addItem(e) {
    e.preventDefault();
    const item = {};
    const inputText = document.querySelector('input[type="text"]');
    item.text = inputText.value;
    item.done = false;
    
    items.push(item);
}

將資料存至 localStorage

接著重點來了,我們要把 items 內的資料存入 localStorage,使用的方法是 localStorage.setItem(..)
這裡要注意的是存入的內容只能是 string,所以要先使用 JSON.stringify(..) 先將 items 轉型別

若不熟悉 localStorage 語法請參考下方 MDN 連結

function addItem(e) {
    e.preventDefault();
    const item = {};
    const inputText = document.querySelector('input[type="text"]');
    item.text = inputText.value;
    item.done = false;
    
    items.push(item);
    localStorage.setItem('items', JSON.stringify(items));
}

最後資料新增了,要記得把輸入匡內容清空。有兩種方法,一種是 inputText.value = '',而另一種是使用表單的方法 this.reset()(這裡 this 指的是表單)

完成後的 addItem 會是這樣

function addItem(e) {
    e.preventDefault();
    const item = {};
    const inputText = document.querySelector('input[type="text"]');
    item.text = inputText.value;
    item.done = false;

    items.push(item);
    localStorage.setItem('items', JSON.stringify(items));

    this.reset();
  }

渲染至畫面

新增新的 function 來處理渲染畫面,也就是將 items 中的內容印出來

function render() {
    // code here
}

資料來源可以直接取用 localStorage 的資料,但如果 localStorage 內沒有資料呢?加上之後其他 function 可能與會取用,所以我們要先修改一下 items 的值。
目前的狀況是,當網頁重整時 items 會變成空 array,所以要修改一下

const items = JSON.parse(localStorage.getItem('items')) || [];

也就是說當網頁讀取時,先看一下 localStorage 中有沒有資料,如果有就取用、但假如沒有就給一個空 array

有了資料來源接下來使用迴圈分別印出 HTML 的內容,並相加至變數 str 當中,最後加至 itemsList.innerHTML

function render() {
    let str = '';
    items.forEach((item, index) => {
        let checked = item.done ? `checked` : '';
      str += `
        <li>
          <input type="checkbox" data-index="${index}" id="item${index}" ${checked}>
          <label for="item${index}">${item.text}</label>
        </li>`;
    });
    
    itemsList.innerHTML = str;
}

可以看到在 forEach 當中多了一個變數 checked,這個變數其實是要判斷選單 itemsList 內的選項是否已被勾選、或是 item.donetrue 的狀況下會在 input[type="checkbox"] 的 tag 中加入 required 的屬性。這裡的寫法是指當 item.done === truechecked = checked ,如果 item.done === falsechecked = ''(空 string)

到這裡渲染畫面的 function 已大致完成,但我們要在剛剛的 addItem 最後加入 render(),也就是在每次新增項目時一併渲染畫面。

function addItem(e) {
    // 上方原 code 省略
     
    render();
}

選單項目打勾

接下來要處理的是當渲染在網頁後並勾選時,需要做到:

  1. localStorage 內的資料更新(item.done = false 或是 true
  2. 畫面重新渲染

選擇監聽元素

這裡你可能會想,既然要監聽有沒有勾選,那就是監聽 input[type="checkbox"] 囉?

不對。你會發現歲然設定監聽,但即便勾選了還是沒也任何反應,這是因為是當你設定監聽時列表內或許根本沒有項目;或是原本有,但當你新增項目後新的項目一樣沒有反應。

因此我們要監聽的元素應該是原本就在,而且無論如何都會存在的元素。沒有錯!就是監聽 itemsList

選擇到 input、'label'... 等等還有 li

但當你實際使用時卻發現不是每次都只選到 input[type="checkbox"],有 label,有時甚至選到 li。原因是 itemsList 內含有許多元素,而 inputlabel 甚至是綁在一起的;為確保每次都是選到 checkbox,我們可以寫 if (e.target.nodeName !== 'INPUT') return; 或是 if (!e.target.matches('input')) return;,意思是當選到的元素不是 input 就 return,如果是才會執行下面的程式碼

function markDoneToggle(e) {
    if (!e.target.matches('input')) return;
    // 若選到的是 input 則執行以下程式碼
    // code here
}

接著如同在 render 做的判斷,當使用者勾選或取消勾選時要做出相對應的動作:

  • 更改 array items 內項目中的 done 的值,如原本是 true 則更改成 false,反之更改成 true
  • 接著更新 localStorage 的內容
  • 更新畫面(render

簡單來說就是先建立一個變數 isChecked 來判斷是否勾選,接著更新 items 以及 localStorage 的內容,最後更新畫面

function markDoneToggle(e) {
    if (!e.target.matches('input')) return;
    
    let isChecked;
    isChecked = e.target.checked ? true : false;
    items[e.target.dataset.index].done = isChecked;
    localStorage.setItem('items', JSON.stringify(items));

    render();
}

到這裡就大致完成啦!但成品我有再新增全選、全取消勾選以及清空列表(包含 localStorage)的功能,算是給你的作業啦!如果途中有問題可以看我的程式碼或問我喔~

Reference


上一篇
Day 14 - JavaScript References VS Copying
下一篇
JS30 Day 16 - Mouse Move Shadow
系列文
一起挑戰 JavaScript 30 吧!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言