0%

JavaScript30-LocalStorage-15

喧鬧、狂歡、輕聲細語、回神早已散場,值得紀錄的一天。

嘿、聲音、呼吸、寧靜,忘了交談了什麼,但笑著入眠。

似乎說了很多很多,但很朦朧。
呆滯地看著眼前,聽不見背景的喧嘩,深刻的只有氣味。

嗨,安穩吧,我想是個安穩的感覺。

從小就覺得不應該說太多,畢竟沈默是金,
多說多錯,講出來好像只會製造麻煩和困擾,
說者無心聽者有意,也許只有這種時候才能毫無保留。

血液有什麼在竄,是酒精帶來的副作用,還是這聞起來令人安心的味道,使我變了樣。
說夢話般的放鬆,想說的全說了,一絲不掛吧,想想還真不可思議,
不經懷疑自己,是壓力太大了只想宣洩,
還是真的有種魔力,像梅杜莎的眼睛會使人石化;主角的笑容永遠燦爛有著不敗金身。

夢醒了,反覆思考著夢裡別人是怎麼看我的,是玩世不恭呢?又或者是貼心的大男孩。
怎麼時間就過了,想著千萬種延續夢境的方法,
好想再去相同的夢境,好想輕靠在舒服的樹蔭,享受著陽光的溫暖徐徐的風,被呵護著,
也許這是我一直想要的,讓我安心地訴說著過去、現在、理想。

很久沒有用腳感受這個社會,不知道是沈靜其中,還是什麼讓我無法思考。
停下來真好,走著走著像是自言自語,心都靜下來了,大約10分鐘的安心,熱血澎湃為藍色星期一換染上淡淡的粉紅。

轉身,揮手,再見,要再見這個夢,好嗎?

在寫這篇的時候,因為有同事要離職,所以莫名其妙地去參加一場活動。
沒想到還蠻有趣的,好久沒有被解放自己的酒精開關,
雖然一開始有點尷尬,但隨著音樂的頻率,好像也會慢慢牽引彼此達到相同的頻率。
一點進步都沒有,只要有酒精就會想睡覺,幸好自己沒有做出什麼丟臉的事情,
隨著參加活動的經驗累積,好像發現自己,只有茫茫的時候會把話匣子打開,
雖然隔天都會意識模糊,忘了為什麼說起;忘了怎麼結束;又怎麼轉場;怎麼遇見太陽;
那時候的我,應該是最真實最沒防備的。

記錄一下這段時間剛好接了一個功能,但由於同事不被Engineer, PM, QA信任,
所以變成事情都交到我身上,在這種況狀下,工作時程的安排,與其他人的溝通,
似乎上次的沈澱慢慢讓自己的成長浮出水面了,
原來在大家眼中我是這麼值得信賴的人,
說來挺開心的,但相對的就更少時間可以做下班學習這件事情了,
這篇文章對我來說很重要,有感覺是經常會需要的東西,每天都會用到的東西,
但知識實在太薄弱了,
只能先硬起來,等全部完成的時候再回來重看一遍好了。
加油吧,不論什麼事情,也不論是什麼人,遇到了就要去處理。
不做永遠不會解決事情,不努力也絕對不會被人喜歡,
是吧? 希望我的努力,是能被看見的,讓我感覺到吧,如果你知道我在努力。

15 - LocalStorage

主題

這篇介紹LocalStorage的用法,
透過一個小菜單來透過localstorage做資料增刪功能。

步驟

Step1. 基礎設定

作者已經設定好這篇練習用的html與css,
主要的架構由一個div包著ulfrom
類似Todo-List的清單(ul)與輸入欄位(form)。

Step2. 撰寫輸入欄位新增功能

首先取得form元素及ul,並宣告一個空陣列來存放新增資料。

1
2
3
const addItems = document.querySelector('.add-items');
const itemsList = document.querySelector('.plates');
const items = [];

接著撰寫一個addItem,參照備註:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function addItem(e) {
// 加上preventDefault()避免每次submit都會重整網頁
e.preventDefault();
// 利用再次querySelector來選取form中的input欄位值
const text = this.querySelector('[name=item]').value;
// 宣告新增要存入的物件,是輸入的文字與是否勾選的狀態(done)
const item = {
text,
done: false
}
console.log(item);
// 清空輸入欄位
this.reset();
}

// 監聽submit按鈕
addItems.addEventListener('submit', addItem);

這樣每次submit後items就會新增在輸入欄位中的物件了!
可透過console.log來查看新增的物件狀態。

Step3. 顯示新增的清單

在上一個步驟中所做的只有存於宣告的陣列中,
並沒有抓出來顯示在HTML中,所以要寫一個function來顯示:

1
2
3
4
5
6
7
8
9
10
11
12
// ES6可在function中的參數直接設定參數預設值
function populateList(plates = [], platesList) {
// 使用map搭配join來組成字串,並顯示在html的清單ul中
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('');
}

然後要記得回到addItem中把platesList放在items.push(item)後面,
讓每次輸入送出後都會執行這個function重新列出組成的物件字串。

Step4. 加入LocalStorage

當完成了新增功能後,就要進入主軸LocalStorage了,
這可以讓瀏覽器存取你設定在這個頁面的資訊,
所以首先在addItem中修改加入這段:

1
2
3
4
5
6
7
function addItem(e) {
//...略
populateList(items, itemsList);
localStorage.setItem('items', JSON.stringify(items));
this.reset();
//...略
}

這裡將items的資訊存在localStorage中一個叫做items的自訂物件中,
注意的是存入的物件或陣列必須透過JSON.stringify轉為字串,
因為localStorage中的值是string,否則直接存只會得到”object object”的字串。

接著修改最一開始宣告的items:

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

讓頁面在重整後,先判斷localStorage中是否有存放items物件,沒有的話則給空陣列。

Step5. 儲存checkbox狀態

這裡要新增一個functiontoggleDone並監聽itemsList的click動作,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function toggleDone(e) {
// 偵測進來的點擊是input(checkbox)才動作
if (!e.target.matches('input')) return;
// 取得checkbox的data-index值
const el = e.target;
const index = el.dataset.index;
// 利用!來使done的狀態在true/false間切換
items[index].done = !items[index].done;
// 將更新後的狀態寫入localStorage中
localStorage.setItem('items', JSON.stringify(items));
// 更新列表
populateList(items, itemsList);
}
// 監聽click
itemsList.addEventListener('click', toggleDone);

Step6. 增加刪除功能

到目前為止只有新增跟儲存的功能,來增加一個刪除按鈕吧,
首先在populateList中字串組成中改成這樣:

1
2
3
4
5
6
7
`
<li>
<input type="checkbox" data-index=${i} id="item${i}" ${plate.done ? 'checked' : ''}/>
<label for="item${i}">${plate.text}</label>
<span data-index=${i}>delete<span>
</li>
`

這會使每次輸出時多一個delete的文字在後方,
然後調整toggleDone

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function toggleDone(e) {
// 初始化一個存檔狀態
let save = false;
// 取得觸發元素的data-index值
const el = e.target;
const index = el.dataset.index;
// 判斷觸發元素,如果是input則為checkbox的狀態切換
if(e.target.matches('input')){
items[index].done = !items[index].done;
save = true;
}
// 如果是span則是透過splice刪除該物件
if(e.target.matches('span')){
items.splice(index, 1);
save = true;
}
// 判斷上方有做事才存擋
if(save){
localStorage.setItem('items', JSON.stringify(items));
populateList(items, itemsList);
}
}

Step7. 新增全選/全取消功能

在HTML的form元素後方加上這段HTML CODE:

1
2
3
4
5
6
7
8
9
10
11
12
13
<style>
.checkMethod {
padding: 0;
text-align: left;
list-style: none;
}
</style>
<ul class="checkMethod">
<li>
<input class="checkAll" type="checkbox">
<label>Check All</label>
</li>
</ul>

使其有多一個checkbox來操作全選/全取消,
接著撰寫對應的功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 取得操作元素
const checkAllBtn = document.querySelector('.checkAll');
// 全選/全取消
const checkAll = function(e) {
// 取得觸發當下全選按鈕是否已勾選
const checkStatus = e.target.checked;
// 透過迴圈將每個item的checkbox狀態改為與全選checobox狀態相同
items.forEach(index => {
index.done = checkStatus;
});
// 存檔
localStorage.setItem('items', JSON.stringify(items));
// 重整
populateList(items, itemsList);
}
// 監聽操作元素動作
checkAllBtn.addEventListener('click', checkAll);

探索

本次探索就是Step6的刪除Step7的新增全選/全取消功能功能擴充,
基本上所有語法都是之前有使用及寫下過的,
LocalStorage很實用,之前做的兩個小練習也都有使用上:

  1. JavaScript練習-臺北市旅遊景點
  2. JavaScript-ETH-Linstener

其他

終於JS30系列完成一半了,當初的目標就是把這系列先練習完,

[DEMO]