JS30 day15 Local Storage and Event delegation
JS30 day15 - JS30 day15 Local Storage and Event delegation
作業內容
今天的內容在講說如何使用 Local Storage 還有如果要指定監聽的對象是動態變化的應該怎麼處理,網頁的部分是透過一個菜單來實作
學到什麼
- JS
preventDefault()參考資料:https://ithelp.ithome.com.tw/articles/10198999- 使用屬性來當作選擇器
this.querySelector('[name=item]')
<input type="text" name="item" placeholder="Item Name" required="">- 使用
this.reset();重置表單 - 在標籤裡面動態插入
<li>標籤:
function populateList(plates = [], plateList){ plateList.innerHTML = plates.map((plate, i) => { return ` <li> <label for="">${plate.text}</label> </li> ` }).join(""); }- 在 html 裡面,如果有一個 checkbox 的 input 以下三種狀況都還是會讓他呈現 checked:true 的狀態
所以如果要根據屬性不同做出不同的 checked 狀態,只能加入 checked 或是空白字元<input type="checkbox" checked> <input type="checkbox" checked="qq"> <input type="checkbox" checked=false>//寫在 js 裡面 <input type="checkbox" data-index=${i} id="item${i}" ${plate.done ? 'checked' : ''}>localStorage是個可以把資料儲存在瀏覽器的空間,不過要記得只能存字串
而一開始的 items 就可以改為先試著從 localStorage 抓資料,沒有的話才給空陣列localStorage.setItem("items", items) //第一個參數是 key 的名字,第二個參數是要存的東西 localStorage.getItem() localStorage.removeItem()const items = JSON.parse(localStorage.getItem('items')) || [];- 我們要去捕捉每一個新長出來的 list 去監聽事件,對這種動態元素,最好的方式是去用他的父元素監聽,利用冒泡機制,所以父元素也會監聽到
但這樣會產生另一個問題,從 console log 可以看到每次都會出現兩個事件,所以必須再去濾除條件function toggleDone(e){ console.log(e.target) } itemsList.addEventListener('click', toggleDone)
function toggleDone(e){ if(!e.target.matches('input')) return; }
參考資料: https://github.com/wesbos/JavaScript30
code 內容:
HTML:
<div class="wrapper">
<h2>LOCAL TAPAS</h2>
<p></p>
<ul class="plates">
<li>Loading Tapas...</li>
</ul>
<form class="add-items">
<input type="text" name="item" placeholder="Item Name" required>
<input type="submit" value="+ Add Item">
</form>
</div>
CSS:
html {
box-sizing: border-box;
background: url('http://wes.io/hx9M/oh-la-la.jpg') center no-repeat;
background-size: cover;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
font-family: Futura,"Trebuchet MS",Arial,sans-serif;
}
*, *:before, *:after {
box-sizing: inherit;
}
svg {
fill:white;
background: rgba(0,0,0,0.1);
padding: 20px;
border-radius: 50%;
width: 200px;
margin-bottom: 50px;
}
.wrapper {
padding: 20px;
max-width: 350px;
background: rgba(255,255,255,0.95);
box-shadow: 0 0 0 10px rgba(0,0,0,0.1);
}
h2 {
text-align: center;
margin: 0;
font-weight: 200;
}
.plates {
margin: 0;
padding: 0;
text-align: left;
list-style: none;
}
.plates li {
border-bottom: 1px solid rgba(0,0,0,0.2);
padding: 10px 0;
font-weight: 100;
display: flex;
}
.plates label {
flex: 1;
cursor: pointer;
}
.plates input {
display: none;
}
.plates input + label:before {
content: '⬜️';
margin-right: 10px;
}
.plates input:checked + label:before {
content: '🌮';
}
.add-items {
margin-top: 20px;
}
.add-items input {
padding: 10px;
outline: 0;
border: 1px solid rgba(0,0,0,0.1);
}
JS:
const addItems = document.querySelector('.add-items');
const itemsList = document.querySelector('.plates');
const items = JSON.parse(localStorage.getItem('items')) || [];
function addItem(e){
e.preventDefault();
const text = (this.querySelector('[name=item]')).value;
const item = {
text: text,
done: false
}
items.push(item);
populateList(items, itemsList)
localStorage.setItem("items", JSON.stringify(items))
this.reset();
}
function populateList(plates = [], plateList){
plateList.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("");
}
function toggleDone(e){
if(!e.target.matches('input')) return;
const el = e.target;
const index = el.dataset.index;
items[index].done = !items[index].done
localStorage.setItem("items", JSON.stringify(items))
}
addItems.addEventListener('submit', addItem)
itemsList.addEventListener('click', toggleDone)
populateList(items, itemsList) // 上面那一列是表單送出才會發生作用,這一列是重新整理或者重新進到這個網頁都會發生作用