Dudi來自印尼, 多年前和丈夫與孩子離別, 隻身一人飄洋過海來台打工, 奮鬥數年後終於事業有成.
遙想當年 Dudi 與丈夫約定, 事業有成時一定要讓他知道. 但眼下 Dudi 在台事業繁忙, 根本無法抽身寫信給丈夫, 聽說最近最潮的東西叫做網頁, Dudi 決定將她想對丈夫說的話藏在網頁之中, 只要有人對著網頁輸入 'dudi' 這個秘密關鍵字, 就會觸發 Dudi 的秘密訊息.
大概像這樣, Dudi!
於是她找上了我們.
寫一段程式碼, 只有當使用者對著網頁輸入 'dudi' 時, 才會觸發 Dudi 預先錄好的愛的訊息.
聽起來我們的目標是偵測一段連續的字串, 只有這段字串正確才會觸發回應的行為.
要偵測一段符合特定規則的序列, 可以設立敲鍵盤事件的監聽器, 讓每次打字時執行以下步驟:
因此程式碼大致如下:
// 我們輸入的字元陣列
const pressed = [];
// 目標字串
const secretCode = 'dudi';
window.addEventListener('keyup', (e) => {
// 將該次打的字推進欲判斷的字串陣列中
pressed.push(e.key);
// 將陣列處理成字串後, 與目標字串比對
if(pressed.join('') === secretCode) {
// 若符合則執行秘密程式碼
speakDudi();
}
})
key
是按鍵事件的屬性, 代表該次按鍵的名稱.
將字元陣列轉變為字串可以用到陣列的 join
方法.
join
方法能夠將陣列中的每個元素連接並合併為一個字串. 唯一的參數 separator
是陣列元素間的連接符號, 預設是逗號 (,).
在此我們不希望字元間有符號, 因此可以放空字串.
到此已有大概架構, 然而這種程式碼, 陣列中的字元會不停累加上去. 這樣的情況下
只允許使用者第一次輸入就輸入正確的目標字串才會觸發秘密程式碼的執行.
要解決此問題, 我們必須限制陣列長度與目標字串長度相等. 然後讓陣列內容循環, 每次輸入新的字元, 最舊的字元就要被洗出陣列, 也就是所謂的FIFO (First In First Out).
最舊的字元會存在陣列的最前方, 最新的則是在最後方. 換句話說, 我們希望每次打字都保留「從末端開始, 和目標字串相同長度的陣列元素」.
如此一來不管之前打錯多少次, 只要一有連續的 'd' , 'u' , 'd' , 'i' 被輸入, 就能夠觸發秘密程式碼了.
修改後的程式碼大致如下:
window.addEventListener('keyup', (e) => {
pressed.push(e.key);
// 加入了這行
pressed.splice(-secretCode.length - 1, pressed.length - secretCode.length);
if(pressed.join('') === secretCode) {
speakDudi();
}
})
在這裡用了splice
方法, 在 Day 7 - Array Cardio II 有介紹到這個方法的用途. 第一個參數是從哪個元素開始切除, 第二個參數是切幾個元素. 切完的結果會直接作用到原陣列上.
一個新技巧是, 當第一個參數為負值時, splice
會從陣列末端倒數要切的陣列.
由於我們希望每次打字都保留「從末端開始, 和目標字串相同長度的陣列元素」. 剛好可以用上, -secretCode.length - 1
跳過了和目標字串相同長度的元素, pressed.length - secretCode.length
就是剩下的量, 都被切掉了.
邏輯大致完成, 最後只要把秘密程式碼完成便可.
function speakDudi() {
dudiVoice.currentTime = 0;
dudiVoice.play();
}
秘密程式碼就是存在 <audio>
的來源裡, Dudi 對分隔多年的家人柔情的吶喊. 在這裡就保留當事人的隱私, 不透露了! 有興趣的可以自行到示範程式碼中輸入秘密關鍵字聆聽.
完成!
以上就是 JS30 第十二篇, 明年見!
##後記
Dudi 等了很多年, 始終沒有等到她老公聽完秘密留言的感想. 失望之餘, 她決定踏上她波瀾壯闊的, 找尋人生第二春的飄渺之旅.
不過 Dudi 忘記了, 她的老家是沒有電腦的...