iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 6
0

完成作品:Type Ahead程式碼

這天預期的結果是當搜尋時搜尋結果與搜尋框相同的文字會被反白,所以嚴格來說我並沒有達成預期的結果,但還是請大家將就一點看了,之後會再回來修改至完善的 QQ。

準備

首先先列出待辦事項:

  • 獲取 API
  • 監聽搜尋框事件
    • 與 API 資料對比是否符合,並為符合的結果建立新的陣列
    • 渲染頁面

開工!

獲取 API 資料

由於我還沒學到 fetchaxios等方法,所以這裡只能先使用最原始的 XMLHttpRequest 來獲取 API 的資料

const endpoint = 'https://gist.githubusercontent.com/Miserlou/c5cd8364bf9b2420bb29/raw/2bf258763cdddd704f8ffd3ea9a3e81d25e2c6f6/cities.json';
let data;  // 用來儲存 API 的資料
function getData() {
    const request = new XMLHttpRequest();
    request.open('get', endpoint, true);
    request.send(null);
    request.addEventListener('readystatechange', (e) => {
      // 當資料成功回傳後執行
      if(e.target.readyState === 4 && e.target.status === 200) {
        data = JSON.parse(request.responseText);
      } else if(e.target.readyState === 4) {
        console.log(`Failed to fetch data.`);
      }
    });
  }

抓下來的每一項內容格式如下:

{
    city: "New York",
    growth_from_2000_to_2013: "4.8%",
    latitude: 40.7127837,
    longitude: -74.0059413,
    population: "8405837",
    rank: "1",
    state: "New York"
}

設定搜尋事件

首先先建立監聽事件,選擇的是之前使用過的 input,這個事件會隨著刪減文字而觸發事件,而我們要在畫面印出搜尋結果

search.addEventListener('input', printResults);

接著來比較搜尋的內容


const search = document.querySelector('input.search');

let cities = [];
let searchText = '';
  
function findMatch(e) {
    searchText = e.target.value.toLowerCase();
    // 檢查資料中是否包含搜尋的關鍵字
    cities = data.filter(cur => cur.city.toLowerCase().includes(searchText) || cur.state.toLowerCase().includes(searchText));
  }

findMatche 指的是該搜尋框,比較的方式是透過將搜尋框的內容先轉成小寫,這裡使用 filter 來篩選,其中與資料的 citystate 作比較,只要有一項包含搜尋的字串就新增到 cities 這個陣列。

比較完成後我們會得到新的陣列 cities,來把結果印出來吧!

印出結果

首先帶入剛剛比較的函式 findMatch,接下來先把列表 (.suggestions) 中的內容清空。當然,這裡也可以不用這樣做,之後再直接把列表的 innerHTML 覆蓋即可。

在印出前先做一下判斷,當搜尋框無內容時恢復預設文字;當無相符結果時印出 "Result not found.";當上述兩種情況以外才印出相符的結果

印出結果的方法我用的是剛學到的 insertAjacentHTML,可以指定插入內容的位置 (beforeend, beforestart, afterend, afterend),詳情見參考連結

最後除了印出 city、state 外還要印出人口,並加上千位數逗號 (例如 1,500,200)
我找到的方法是使用 Number 內建的方法 toLocaleString(),使用正規表達式也可以達到一樣效果

const suggestions = document.querySelector('.suggestions');

function printResults(e) {
    findMatch(e);

    suggestions.innerHTML = '';
    if(searchText === '') {
      suggestions.insertAdjacentHTML('beforeend',`<li>Filter for a city</li><li>or a state</li>`);
    } else if (cities.length === 0) {
      suggestions.insertAdjacentHTML('beforeend',`<li>Result not found.</li>`);
    } else {
      cities.forEach(cur => {
        // 為數字加上千位數逗號
        const formatedPopulation = Number(cur.population).toLocaleString();
        suggestions.insertAdjacentHTML('beforeend', 
          `
            <li>
              <span class="name">${cur.city}, ${cur.state}</span>
              <span class="population">${formatedPopulation}</span>
            </li>
          `
        );
      });
    }
}

到這裡就 "大概" 完成了,之後規劃是做出 highlight 功能、使用其他方式獲取 API 資料 (像是 fetch),以及新增多點搜尋的比較條件。感謝收看~

Reference


上一篇
JS30 Day 5 - Flex Panel Gallery
下一篇
JS30 Day 7 - Array Cardio Day 2
系列文
一起挑戰 JavaScript 30 吧!30

尚未有邦友留言

立即登入留言