JS30 Day 15 筆記


Posted by GL on 2023-05-25

目標

模擬餐廳菜單,在頁面中增加菜名,并且用 localStorage,使頁面重新整理之後仍保留原來菜名

Demo

播放

step 1 : 獲取 HTML 元素,並用變數儲存菜單資料

// 拿到 form 元素
const addItems = document.querySelector('.add-items')
// 拿到顯示菜單的列表 ul 元素
const itemsList = document.querySelector('.plates')
// 變數 items 為所有菜單内容,預設空陣列
const items =  []

step 2 : 監聽 form 的 submit 事件

 function addItem(e) {
    // preventDefault() 避免 submit 預設行爲發生
    e.preventDefault();
    // 拿到 form 中 input 欄位的值
    const text = this.querySelector('[name=item]').value;

     // 宣告變數 item 為新增的菜單資料,包含 input 輸入的值以及勾選狀態(done)
    const item = {
      text,
      done: false
    }
    console.log(item);

    // 清空 input 欄位
    this.reset();
  }

  // 監聽 form 的 submit 按鈕,並觸發 addItem function
  addItems.addEventListener('submit', addItem);

step 3 : 建立 populateList function,將資料顯示在 HTML 中

function populateList(plates = [], platesList){
    // map() 方法將每筆資料帶入 li 元素中,加上 join() 連接字串
  platesList.innerHTML = plates.map((plate,index)=>{
    return `
      <li>
        <input type="checkbox" data-index=${index} id="item${index}" ${plate.done? 'checked': ''}/>
        <label for=item${index}>${plate.text}</label>
      </li>
    `
  }).join('')
}
 // 新增菜單
 function addItem(e) {
    e.preventDefault(); // 避免 submit 的預設行爲且表單送出之後的重新整理
    const text = this.querySelector('[name=item]').value;
    const item = {
      text,
      done: false
    }
    console.log(item);
    // 將資料 push 到變數 items 陣列中儲存
    items.push(item)
    // 執行 populateList function, 並帶入參數 items, itemsList
    // 讓資料顯示在畫面中
    populateList(items, itemsList)

    this.reset()
  }

  // 監聽 form 的 submit 按鈕,並觸發 addItem function
  addItems.addEventListener('submit', addItem);

step 4 : 設定 localStorage

localStorage.setItem('name', 'value'):儲存(用 JSON.stringify 將物件轉為 string)
localStorage.getItem('name', 'value'):取值 (用JSON.parse()將 string 轉回來)

function addItem(e) {
  ...
  populateList(items, itemsList);

  // 將 items 的資訊儲存於browser 的 localStorage 中,
  // 一個自訂 key 為"items",value 為變數 items 的物件
  // localStorage 的值存的是字串,記得用 JSON.stringify 先轉爲字串
  localStorage.setItem('items', JSON.stringify(items));

  this.reset()
  ...
}

回到一開始宣告的變數 items,先判斷 browser 的localStorage 是否有 items 物件的值,若 localStorage 沒有此值,則設爲空陣列

 // 記得將拿到的值從字串格式轉換為 JSON 
const items = JSON.parse(localStorage.getItem('items')) || []

step 5 : 處理 checkbook 狀態

  function toggleDone(e) {
     // 這裡的 e.target 可能返回 input 及 label 兩個元素
    // 偵測到的是 input 才執行 (<input type="checkbox".../>)
    if (!e.target.matches('input')) return;

    // 拿到 checkbox 中的 data-index 值
    const el = e.target;
    const index = el.dataset.index;

    // 透過 items 和 index 可以得到 li 的資料
    // 用 '!' 做一個 switch (若 done 為 true => 改為 false,反之亦然)

    items[index].done = !items[index].done;

    // 同步更新 localStorage 中的狀態
    localStorage.setItem('items', JSON.stringify(items));
    // 同步更新頁面的狀態
    populateList(items, itemsList);
  }

  // 利用事件代理機制(Event Delegate),將監聽器掛在 父元素 ul 上,
  // 監聽子元素 li 的click 事件,觸發時執行 toggleDone function
  itemsList.addEventListener('click', toggleDone);

補充: 客制化 checkbox


/* checkbox 先不顯示 */
input[type='checkbox'] {
  display: none;
}

/* 沒有 checked 時用僞元素的方式添加假的 checkbox */
input[type='checkbox'] + label:before {
  content: '⬜️';
  margin-right: 10px;
}

/* checked 時用僞元素的方式添加假的 checkbox */
input[type='checkbox']:checked + label:before {
  content: '🌮';
}

參考資料:


#JS 30







Related Posts

Day 01 七天學會基本演算法(一)踏入演算法學習前應該了解的資料結構

Day 01 七天學會基本演算法(一)踏入演算法學習前應該了解的資料結構

曼陀號領航計畫(2) Climb the ladder like Spider-Man

曼陀號領航計畫(2) Climb the ladder like Spider-Man

MTR04_0616

MTR04_0616


Comments