JS 30 是由加拿大的全端工程師 Wes Bos 免費提供的 JavaScript 簡單應用課程,課程主打 No Frameworks
、No Compilers
、No Libraries
、No Boilerplate
在30天的30部教學影片裡,建立30個JavaScript的有趣小東西。
另外,Wes Bos 也很無私地在 Github 上公開了所有 JS 30 課程的程式碼,有興趣的話可以去 fork 或下載。
利用 Fetch API
取得 JSON
格式的資料,隨著輸入關鍵字的不同,將特定資料篩選出來並呈現在網頁上。
將要取得的資料來源網址放入 endpoint
中,並建立一個空的city
陣列。
const endpoint = 'https://gist.githubusercontent.com/Miserlou/c5cd8364bf9b2420bb29/raw/2bf258763cdddd704f8ffd3ea9a3e81d25e2c6f6/cities.json';
const cities = [];/*empty array*/
fetch()
是 Fetch API
的一個方法,主要用於發出資料的 request
並回傳 Promise
,有點類似於XMLHttpRequest
,但在使用上較有彈性。
Promise
物件代表的是一個將要完成或失敗的非同步操作,以及它所產生的值。
then()
方法最多需要兩個參數,分別作為 Promise
成功和失敗時的 callback function
並返回 Promise
。
json()
方法會接收一個 response stream
,在讀取完成之後返回一個解析成 JSON
格式的 Promise
。
我們透過 fetch()
向 endpoint
發送 request
,之後將 return 的 Promise
以 json
格式進行解構,最後將這些資料一筆筆的 push 到空的 cities
陣列中。...data
的主要用意在於將原本的 data
陣列中的元素個別拆出來放進 cities
裡面,如果直接寫cities.push(data)
則 data
會變成在 cities
內部的一個陣列型態元素。
fetch(endpoint)
.then(blob => blob.json())
.then(data => cities.push(...data));
findMatches()
需要用到兩個參數,wordToMatch
用來傳入要尋找的文字,cities
則是原本的資料陣列,最後回傳經過 filter()
過濾後的 cities
陣列。
在 filter()
裡,我們將 cities
的每一個元素用 RegExp
物件進行正規表示式的字元比對,之後將包含關鍵字的 city
或是 state
回傳。
RegExp()
用來建立一個正規表達式的物件,wordToMatch
是要進行比對的內容,'gi'
則是 flag,g
代表搜尋出所有符合比對的文字,而不是比對出第一筆就停止,i
代表比對時不分大小寫。
function findMatches(wordToMatch, cities){
return cities.filter(place => {
const regex = new RegExp(wordToMatch,'gi');
return place.city.match(regex) || place.state.match(regex);
})
}
numberWithCommas()
主要用來幫我們在每三位數字加上逗號(ex.1,000)。toString()
可以幫我們將 x 轉成字串,之後利用 replace()
進行正規表示式的判斷並取代字元。
function numberWithCommas(x){
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g,',');
}
宣告 matchArray
並放入找到的結果陣列。接著,將陣列中的元素換成 HTML
的格式。再來宣告一個 Regex
物件,然後宣告兩個常數並各自以正規表示式判斷並取代(replace)原有的文字內容,改以 HTML
的方式呈現。
之後,透過 Template literals
的格式傳回一個<li>...</li>
。最後,使用 join()
將陣列中的所有元素以空字串為分隔,形成一個很長的字串再放入.suggestions
的標籤中。
function displayMatches(){
const matchArray = findMatches(this.value,cities);
const html = matchArray.map(place =>{
const regex = new RegExp(this.value, 'gi');
const cityName = place.city.replace(regex,`<span class="hl">${this.value}</span>`);
const stateName = place.state.replace(regex,`<span class="hl">${this.value}</span>`);
return `
<li>
<span class="name">${cityName}, ${stateName}</span>
<span class="ppulation">${numberWithCommas(place.population)}</span>
</li>
`
}).join('');
suggestions.innerHTML = html;
}
分別宣告常數 searchInput
、suggestions
並取得 .search
、.suggestions
標籤。最後為搜尋欄(searchInput
) 註冊change
和keyup
事件,當欄位數值發生改變或放開鍵盤的那個剎那都會觸發事件,兩個事件都是以 displayMatches()
進行事件處理。
const searchInput = document.querySelector('.search');
const suggestions = document.querySelector('.suggestions');
searchInput.addEventListener('change',displayMatches)
searchInput.addEventListener('keyup',displayMatches);
Promise
使用 Fetch
Promise.prototype.then()
Response.json()
Spread syntax (...)
RegExp()
比較 keydown, keypress, keyup 的差異