iT邦幫忙

1

JS30 Day 6 - Type Ahead學習筆記

這次的章節,對於正規表達式不熟悉的我,是個非常難理解的,花了大概半天才理解清楚,程式碼全部的意思,也希望如果有地方寫得不好,請見諒,或是大大能夠補充,謝謝!

這次的功能是很常見的搜尋功能,給予關鍵字,並會在搜尋欄下面呈現符合條件的資料。

fetch vs XMLHttpRequest

首先我們要獲取資料,排除axios之外,獲取資料又有分為兩種方式

一種為fetch,一種為XTMLHttp,兩者最大差異在於一個用callback,一個用promise
fetch雖操作較XML簡單,但目前不支持進度條,超時,錯誤不會被拒絕。
XMLHttpRequest雖較複雜,但支持許多功能

XMLHttpRequest寫法:


    function requestHandler() {
      if (this.readyState !== 4) return;
      if (this.status === 200) {
        console.log(JSON.parse(this.response));
      } else {
        console.log('HTTP error');
      }
    }

    let req = new XMLHttpRequest();
    req.addEventListener('load', requestHandler);
    // endpoint為要獲取的資料
    req.open('GET', endpoint);
    req.send();
    

fetch寫法:

    // endpoint為要獲取的資料
    fetch(endpoint)
      .then(item => item.json())
      .then(data => console.log(data))
      .catch(error => console.log(error));

fetch獲取資料又有兩種方式:一是直接賦值,二是解構後放進去。

let為變數宣告,除非我們所設的值是可變的用let。
const為常數宣告,在設置常數,陣列,物件,函數等類型都用const。

直接賦值

    // 直接設置空值,賦值並獲取資料
    // 由於cosnt一定要有值,所以不可用const宣告
    
    let cities = null;
    fetch(endpoint)
      // 將資料解析(json())
      .then(item => item.json())
      .then(data => cities = data)
      // 已成功獲取資料
      .then(cities => console.log(cities));

解構賦值

    // 一種設置空陣列,並利用解構一個個放進去
    const citiess = [];
    fetch(endpoint)
      .then(item => item.json())
      .then(data => citiess.push(...data));
    

獲取完資料後,我們要獲取輸入的關鍵字,也就是獲取我們的input並增加監聽事件,當關鍵字改變時觸發,input、或keyup事件也可不用change事件,因為在< input >,< select >,< textarea >內的值改變時且不是 focus 狀態時觸發,也就是要當鼠標不在欄位,才會觸發,故不適合。

    document.querySelector('.search').addEventListener('input', inputHandler);

而在觸發後,呼叫一個函數,用來處理資料,如過濾資料、關鍵字的反黃,人口數的格式等。
首先,將我們的關鍵字以及全部資料傳入至一個專門處理資料,但不影響原資料的Purefunction。

      // 將我們所輸入的值(第一個參數),及全部的資料(第二個參數),傳入findMatches並進行過濾
      const matchArray = findMatches(this.value, cities);

RegExp

正則表達式就是用於查找符合某些複雜規則的字符串的工具,在 JavaScript 中,正規表達式也是物件。
常見的寫法有兩種:

一種為: 用RegExp常數,包含兩個 / 字元之間的模式

const regex = /bc/gi;

一種為: 呼叫RegExp物件的建構子函數

const regex = new RegExp('los', 'gi');

正規表達之特殊字元:

正規表達模式由數個簡易字元組成,例如 /abc/,或是由簡易字元及特殊符號組合而成,每種組合都代表不同意思

\B:放在前面,代表前面要有東西(英文、數字、_),放在後面反之。
\b :放在前面,代表前面要沒有東西,後面反之。
(?=xxx):要符合xxx。
\d:d是數字,這邊是指所有數字0-9。
{}:代表字元出現次數。
+:代表+前面的字元可出現1次或多次。
(?!=xxx) : 不能符合xxx。
g : 會匹配全部

extra common

^ : 要符合在首位,ex:/^B/,Beach符合,DoBule不符合
$ : 要符合在末位,ex:/d$/,bad符合,dark不符合
. : 只要沒有換行符號都符合,ex:/.a/,w a h符合,abc不符合
i : 不分大小寫匹配
米字號 : 放在字後面,代表有0個對應字或多個字,ex:/ab*c/,abbbc符合,acb不符合

本實作程式碼:
由後往前比較,g為匹配全部的資料,而最後一個不能為數字,且前面必須為三個數字,且可以為一組以上,而最前面要有東西結論就是最前面要有東西,且要符合每三個數字為一組,至少一組(三個數字)以上,且最後面不為數字才符合

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

match():此方法會回傳一含比對成功字串為元素的陣列,若無符合字元則回傳null。

他會利用正規表達式來比對搜尋字串

string.match(regexp)

參數為regexp(正規表達式物件),如果不是,會被隱式的使用new RegExp(obj)轉化成正規表達式物件。

purefunction

而下面這段代碼,在const regex部分,創建一個正規表達式,第一個參數為我們所輸入的關鍵字,
第二個參數g為比對全部資料,i為不分大小寫,而在最後return部分,就是在比對資料,符合的話就返回。

    // purefunction,也就是不會改變這個function以外的東西(如word,cities),
    // 經過這個function,word,cities不變,只是拿進來使用,處理完後,最後回傳一個陣列。
    function findMatches(word, cities) {
      return cities.filter(place => {
        // 利用正規表達式,去篩選符合條件的資料
        const regex = new RegExp(word, 'gi');
        // 只要其中一項(資料的城市或地區)符合,就回傳以陣列型式且符合的資料
        return place.city.match(regex) || place.state.match(regex)
      });
    }

replace :此方法會尋找符合的字元,並將他們以新的字元去做替換。

注意replace如果第一個參數為正則,可以把全部都替換,如果單純用replace,換完第一個就會停止

const a = '88cdefg88';
const regex = new RegExp('88', 'gi');

參數為正則,全部替換
a.replace(regex,gg); // ggcdefgg

參數為常數,替換一個就停止
a.replace('88',gg); // ggcdef88

toLocaleString:將數值型別的資料,更改為不同的顯示方式,預設為英文語系,故每三個數字一個逗號

此外,除了使用正規表達式來設置數字格式之外,我們還可利用toLocaleString來達到效果。


function populationHandler(num) {
      // 因為在json中資料(population)是以字串型式,所以為了使用toLocaleString,
      // 要將資料轉為數值,故利用*1強制轉型即可
       return (num * 1).toLocaleString();
};

join():join將字串以括號內的元素合成一個字串

不加上參數的話預設以,隔開,此處用空不加上join的話,每個Li中間都會有逗號隔開(因為陣列關係)。
如下圖
https://ithelp.ithome.com.tw/upload/images/20200518/201261828iNu6dn4KY.png

    function inputHandler() {
      // 將我們所輸入的值(第一個參數),及全部的資料(第二個參數),傳入findMatches並進行過濾
      const matchArray = findMatches(this.value, cities);
      console.log(matchArray); // 會獲取條件過濾後的資料
      // 獲取過濾完的資料後,以列表呈現在搜尋框下
      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="population">${numberWithCommas(place.population)}</span>
      </li>
    `;
        // join將字串以括號內的元素合成一個字串,預設為,,此處用空
        // 不加join的話每個Li中間都會有逗號隔開(因為陣列關係)
      }).join('');
      console.log(html);
      document.querySelector('.suggestions').innerHTML = html;
    }

而我們再拿到過濾完的資料後,需要將資料呈現出來,因為回傳的資料是以陣列型式
,故利用map對每個資料做處理,利用replace方法去改變每筆符合資料,關鍵字的顏色
,並回傳一個以正規表達式處理好的人口數,然後將整個陣列,用join方法切割並合併
成一個字串,最後放進ul裡面即可

完整結果如下圖

https://ithelp.ithome.com.tw/upload/images/20200518/20126182KLlP4mkdSW.png


尚未有邦友留言

立即登入留言