Day 6 - Ajax Type Ahead

前言

JS 30 是由加拿大的全端工程师 Wes Bos 免费提供的 JavaScript 简单应用课程,课程主打 No FrameworksNo CompilersNo LibrariesNo Boilerplate 在30天的30部教学影片里,建立30个JavaScript的有趣小东西。

另外,Wes Bos 也很无私地在 Github 上公开了所有 JS 30 课程的程序码,有兴趣的话可以去 fork 或下载。


本日目标

利用 Fetch API 取得 JSON 格式的资料,随着输入关键字的不同,将特定资料筛选出来并呈现在网页上。


解析程序码

JS 部分

将要取得的资料来源网址放入 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 的 Promisejson 格式进行解构,最後将这些资料一笔笔的 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;
}

分别宣告常数 searchInputsuggestions 并取得 .search.suggestions标签。最後为搜寻栏(searchInput) 注册changekeyup事件,当栏位数值发生改变或放开键盘的那个刹那都会触发事件,两个事件都是以 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 的差异

范例网页请按此


<<:  失败了 还是可以进行更新

>>:  Day-6 Excel以等比级数填满!?

Day 5 | 游戏流程与关卡设定

今天要为各位介绍我们的游戏流程与关卡设定 游戏流程 上图是游戏的流程图,我们的游戏采单一故事线的剧情...

30天零负担轻松学会制作APP介面及设计【DAY 21】

大家好,我是YIYI,今天我要来制做设定页面。 从哪里可以进入设定页面呢? 点击LIST的设定~如下...

D04 / 可不可以用 ConstraintLayout - ConstraintLayout

今天大概会聊到的范围 Constraint Layout in Compose 上一篇提到,有 R...

【Day18】浏览器物件模型--BOM

我们学习前端语言就是为了跟浏览器沟通,让网页可以渲染出想要的效果,创造使用者互动的良好体验。所以要...

【Mac清理】如何找出与其他ios端同步後的重复图片

Mac 电脑里有很多重复的图片?有时候我们可能会重复汇入或下载相同的图片却没有及时清理掉。另外,如果...