iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 6
0

Day6-課題內容

進入JS30的第六天,今天要將第四天學到的陣列方法,結合正規表達式,實作出有篩選功能的表單頁面。
在作者的範例中,當我們輸入英文字母時,下方將會顯示出相符的城市資訊,並且相同的地方會被強調出來。
實作連結

取得資料

其實作者尚未點開接取API資料的技能,所以一開始的時候,是將網址內的資料複製到檔案內。到目前也只知道照著做就能取得API資料,因此這邊先不多加贅述。完成影片中的步驟後,我們會取得一由物件所組成的陣列,物件中的屬性為美國各城市的相關資料。

const endpoint = 'https://gist.githubusercontent.com/Miserlou/c5cd8364bf9b2420bb29/raw/2bf258763cdddd704f8ffd3ea9a3e81d25e2c6f6/cities.json';

let locationData = [];

fetch(endpoint)
  .then(function (blob) {
    return blob.json();
  }).then(function (data) {
    locationData = data;
  });

監聽事件

首先我們先將指定的元素加上監聽事件,當我們在搜尋區塊輸入字母時,便觸發事件並執行函式。而這邊我們將<input>元素的監聽事件,設為前面提過的oninput事件,並加上執行的函式displayMatches()

document.querySelector('input').addEventListener('input', displayMatches);

撰寫函式

在我們執行的displayMatches()函式中,需要包含以下的功能:

  1. 判斷符合的物件
  2. 將符合的資料呈現在頁面上
  3. 將符合的字體加上顏色

符合的物件

搜尋欄位的輸入值與陣列中的物件屬性我們知道如何取得,但是輸入的字母可能同時包含大小寫,要怎麼輕鬆的比對這兩筆資料呢?這時就要帶大家來認識正規表達式。

正規表達式

正規表達式是一種被用來比對字串中字元組合的模式。[2][3]

  1. 正規表達式內容:
    pattern:當作字串搜尋的比對規則。
    modifier:當作字串搜尋的比對方式。g:表示查詢全部文本 (global match),i: 表示不限大小寫 (ignore case)。
/pattern/modifiers;

下圖是正規表達式的標準格式,而pattern中含有非常多可以利用的特殊字符,可以組合出不同比對規則。[4]

  1. 建立正規表達式的方法:
//1. 使用RegExp常數,包含兩個 / 字元之間的模式
var re = /ab+c/gi;
//2. 呼叫RegExp物件的建構子函數
var re = new RegExp("ab+c", "gi");
  1. 字串利用正規表達式的相關功能:
  • match():此方法會回傳一含比對成功字串為元素的陣列,若無符合字元則回傳null
var test = new RegExp("a","gi");
let a ='abcAbca'
console.log(a.match(test));
//['a', 'A', 'a']
  • search():此方法會回傳第一個符合字元的index,若無符合字元則回傳-1。
var test = new RegExp("ba","ig");
let a ='bcAbca'
console.log(a.search(test));
//2
  • replace():此方法會尋找符合的字元,將他們取代成設定的新字元。
var test = new RegExp("ab","ig");
let a ='ababab'
console.log(a.replace(test, "cd"));
//cdcdcd
  • split():此方法會將符合的字元作為切割點,將原本的字串切割成一個陣列。
var test = new RegExp("bc","ig");
let a ='abcdabcdabcd'
console.log(a.split(test));
//["a", "da", "da", "d"]

了解正規表達式之後,我們就可以在函式加入正規表達式,利用他來幫助我們判斷符合的物件。透過event.target.value方法,我們可以取得<input>元素觸發事件時的值,並且利用第四天所學的array.fitler()方法,結合正規表達式的功能,過濾出包含符合字元的物件的陣列:

function displayMatches(event) {
  let checkWord = event.target.value;
  const matchArray = cities.filter(function(place) {
    const regex = new RegExp(checkWord, 'gi');
    return place.city.match(regex) || place.state.match(regex);
  }); 
};

將資料呈現在頁面上

要將資料呈現在頁面上,就要透過在HTML上新增元素,而目前的HTML中DOM的結構如下:

<form class="search-form">
    <input type="text" class="search" placeholder="City or State">
    <ul class="suggestions">
      <li>Filter for a city</li>
      <li>or a state</li>
    </ul>
</form>

在觸發函式之後,我們需要將原本ul容器中的<li>元素清除,然後加上含有符合的資料的<li>元素,因此我們將透過修改ul容器中的innerHTML,將我們符合的資料呈現在頁面上。透過array.map()方法,使每一個符合的物件,轉為我們需要的資料格式。但因array.map()方法最後回傳的值為一個陣列,因此需要透過.join('')將陣列轉為字串格式並消除之間的陣列元素之間的,,最後再填入ul元素中,否則陣列中的,會因被加入HTML中而被呈現在頁面上。
將相符字元轉換顏色的方法,則是利用前面提到的replace()方法。透過將原本的字元取代成<span class = "hl" ${checkWord} </span>,我們就可以將原本的字元處理成<span>元素,並透過class將CSS屬性加上。將每個步驟的code結合起來的內容如下:

// input元素監聽事件
document.querySelector('input').addEventListener('input', displayMatches);

//選取ul元素
const suggestions = document.querySelector('.suggestions');

//觸發的函式
function displayMatches(event) {
    let checkWord = event.target.value;
    //篩選符合的物件                          
    const matchArray = cities.filter(function(place) {
    //設定正規表達式的篩選條件為全文以及不分大小寫
    const regex = new RegExp(checkWord, 'gi');
    return place.city.match(regex) || place.state.match(regex);
    });  
    //將需要的資料轉成html格式
    const html = matchArray.map(function(place) {
    const regex = new RegExp(checkWord, 'gi');
    //將匹配的字元獨立一個<span>元素並加上class
    const cityName = place.city.replace(regex, `<span class="hl">${checkWord}</span>`);
    const stateName = place.state.replace(regex, `<span class="hl">${checkWord}</span>`);
    //設定回傳元素的資料格式
    return `
      <li>
        <span class="name">${cityName},${stateName}</span>
        <span class="population">${(place.population)}</span>
      </li>
    `;
    //將陣列轉為字串
    }).join('');
    //將`ul`容器中的HTML內容改為符合的資料內容
    suggestions.innerHTML = html;
};

修改人口呈現方式

作者在最後加上了一個正規表達式,利用replace()將人口數中每三個數字的位置中間加入,,我們來看看這個正規表達式做了什麼。這邊先提供一個參考網站,他可以偵測條件以及符合的字串,讓各位在使用時會比較好理解及判斷。[5]

function numberWithCommas(x) {
  return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); ''
};

此正規表達式/\B(?=(\d{3})+(?!\d))/g中使用到的特殊符號:

  1. \B:尋找字串中符合的位置。
  2. ?=:字串的篩選條件。
  3. \d:d是數字(digit)的意思,這邊是指所有數字0-9。
  4. {}:代表字元出現次數。
  5. +:代表+前面的字元可出現1次或多次。
  6. x(?!y):符合'x',且後接的不是'y'。

我們可以得知\B(?=...)是要尋找符合篩選條件的字串位置,而字串的篩選條件為(\d{3})+(?!\d)(?!\d)代表的是非數字的地方,而(\d{3})代表每三個數字為一組,透過+組合起來的意思即為以每三個任意數字為一組,從最後非數字的地方,往前尋找一組或多組以上的間隔位置。再將此位置透過replace()轉換成,,就可以出現作者範例中的樣式囉。

總結

在今天課題當中,我們學到以下的技能:

  1. 實作array.filter()以及array.map()使用資料。
  2. 正規表達式的概念與應用。
  3. 透過修改元素中的innerHTML將資料呈現在頁面上。

在今天的範例中,我們成功將一比資料,透過正規表達式以及陣列的方法,將需要的資料篩選出來並呈現在頁面上,而這些都算是web前端工程師的基礎技能,希望有志成為web前端工程師的讀者,都可以跟我們一起來熟悉這些功能。以上是今天的實作與感想,感謝您的閱讀。

參考資料

  1. javascript30
  2. MDN-正規表達式
  3. w3school-JavaScript RegExp Reference
  4. 文本分析基礎-正規表達式(REGEXP)
  5. online regex tester

上一篇
JS30-Day5-Flex Panels
下一篇
JS30-Day7-Array Cardio 2
系列文
新手也能懂的JS3030

尚未有邦友留言

立即登入留言