iT邦幫忙

2023 iThome 鐵人賽

DAY 15
0
Modern Web

JS30 x 鐵人30 x MDN doc系列 第 15

[Day15] - LocalStorage(JS30 x 鐵人 30 x MDN)

  • 分享至 

  • xImage
  •  

透過 Localstorage 儲存項目,新增、狀態切換、刪除(不在原題內)

觀察 index-Start.html 的 javascript 只有以下三行,看來需要利用items這個陣列的元素去產生結構並於itemList這個節點中顯示。

const addItems = document.querySelector(".add-items");
const itemsList = document.querySelector(".plates");
//  看到第5點後記得再回來更改
const items = [];
  1. 資料格式:先定義一下items中每一項資料的格式吧:
//  {
//    text:String,//  要顯示於頁面上的文字,所以是字串
//    done:Bolean,//  用來記錄input是否勾選的狀態,所以是布林值
//  }
  1. 資料新增(監聽器):利用以上資料格式:針對addItems這個節點新增一個針對HTMLFormElement: submit event監聽器,事件觸發後會執行addItem函式,另外因為submit事件的預設行為會將表單的填入內容添加到網址列並送出,會使頁面重整,但這不是我們想要的效果因此,我們使用Event: preventDefault() method將預設行為禁用,並自己寫要執行的邏輯。
addItems.addEventListener("submit", addItem);

function addItem(e) {
  //  禁用預設行為
  e.preventDefault();
  //  從表單內取得添加項目的資料
  const text = this.querySelector('input[name="item"]').value;
  const item = {
    text,
    done: false,
  };
  //  更新至items陣列
  items.push(item);
  //  清除表單已填的資料
  this.reset();
  //  看到第5點後記得再回來新增後續邏輯
  //  .
  //  .
}
  1. 渲染函式:資料也有了,也能新增了,接下來就是渲染製畫面的部分,但是每個元素的結構作者已有設定並且搭配 CSS 中的樣式,所以我們偷看一下FINISHED.html新增品項後產生的結構,然後著手改寫每當資料有變更的時候都需要觸發的渲染函式renderItems吧,這邊我一樣不使用 innerHTML 的方式,全部用 creatElement 並設定屬性方式讓各位看看程式碼數量上的差異。
function renderItems(itemsList, items) {
  //每次渲染進itemsList之前都先清空內的全部內容
  while (itemsList.firstChild) {
    itemsList.removeChild(itemsList.firstChild);
  }
  //創造一個文檔片段
  const fragment = document.createDocumentFragment();
  //每一項都創造相同的結構:一個<li>裡面有<input>、<label>、<i>
  items.forEach((element, index) => {
    const li = document.createElement("li");
    li.id = index;
    //<input>元素
    const input = document.createElement("input");
    input.type = "checkbox";
    input.id = `item${index}`;
    input.defaultChecked = element.done;
    input.checked = element.done;
    li.appendChild(input);
    //<label>元素 CSS中作者將input隱藏起來,並使用這個label代替input的功能
    const label = document.createElement("label");
    //HTML中直接寫的for 若改用node屬性設定則要設定htmlFor
    label.htmlFor = `item${index}`;
    label.textContent = element.text;
    li.appendChild(label);
    //<li>元素,我自己額外做的刪除功能不在原題目需求內
    //(用到fontawesome icon)請記得在html內引入
    const i = document.createElement("i");
    i.classList.add("fa-solid", "fa-trash-can");
    li.appendChild(i);
    fragment.appendChild(li);
  });
  itemsList.appendChild(fragment);
}
  1. 點擊事件(監聽器):接著我們針對品項清單添加一個針對 click 事件的監聽器,事件觸發後會執行handleInputClick函式,只有當觸發事件的元素是<label><i>時才會做出該項 item 的切換狀態或刪除。
itemsList.addEventListener("click", handleInputClick);

function handleInputClick(e) {
  // 觸發元素是label時,反轉該項的完成Done狀態
  if (e.target.matches("label")) {
    items[e.target.parentNode.id].done = !items[e.target.parentNode.id].done;
    //  看到第5點後記得再回來新增後續邏輯
    //  .
    //  .
    // 觸發元素是i時,將該項從陣列中移除
  } else if (e.target.matches("i")) {
    items.splice(e.target.parentNode.id, 1);
    //  看到第5點後記得再回來新增後續邏輯
    //  .
    //  .
  }
}
  1. localStorage 存取:最後才是本日的重點Window: localStorage,要注意因為LocalStorage只能存放 UTF-16 格式字串,但我們的資料是陣列裝著物件,因此我們在讀取時須透過JSON.parse()解析資料,儲存時使用JSON.stringfy()轉成 JSON 字串。
  • 於是我們將一開始的 items 改寫成以下。
//  當頁面載入時如果localstorage已經有資料那就拿過來使用,如果沒有則預設為一個空陣列
const items = JSON.parse(localStorage.getItem("items")) || [];
//  並拿這個items去執行一次渲染函式
renderItems(itemsList, items);
  • 最後在有變更到items陣列資料時(handleInputClick函式、addItem函式)後面都補上以下兩句。
//  將變動後的資料覆寫進localStorage
localStorage.setItem("items", JSON.stringify(items));
//  再次執行渲染函式,更新畫面
renderItems(itemsList, items);

👉Github Demo 頁面 👈

👉 好想工作室 15th 鐵人賽看板 👈

參考資料

  1. Javascript 30 官網
    https://javascript30.com/
  2. MDN 官網
    https://developer.mozilla.org/en-US/

上一篇
[Day14] - JavaScript References VS Copying(JS30 x 鐵人 30 x MDN)
下一篇
[Day16] - Mouse Move Shadow(JS30 x 鐵人 30 x MDN)
系列文
JS30 x 鐵人30 x MDN doc30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言