iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 6
0
自我挑戰組

新手村-30 Day JS Coding Challenge系列 第 6

新手村06 - Type Ahead

06 - Type Ahead

俗話說的好,一天一蘋果,醫生遠離我

一天一 JS,What the f*ck JavaScript?

small steps every day - 記錄著新手村日記

完成目標

  • 功能
    • 接收到 Json
    • 列出符合關鍵字的項目
  • 畫面
    • 列出符合關鍵字的項目
    • 將關鍵字的部份mark起來
    • 顯示該項目的「人口」數字

index_START.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Type Ahead ?</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>

  <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>
<script>
const endpoint = 'https://gist.githubusercontent.com/Miserlou/c5cd8364bf9b2420bb29/raw/2bf258763cdddd704f8ffd3ea9a3e81d25e2c6f6/cities.json';

</script>
</body>
</html>

CSS

/* style.css */

html {
  box-sizing: border-box;
  background: #ffc600;
  font-family: 'helvetica neue';
  font-size: 20px;
  font-weight: 200;
}

*, *:before, *:after {
  box-sizing: inherit;
}

input {
  width: 100%;
  padding: 20px;
}

.search-form {
  max-width: 400px;
  margin: 50px auto;
}

input.search {
  margin: 0;
  text-align: center;
  outline: 0;
  border: 10px solid #F7F7F7;
  width: 120%;
  left: -10%;
  position: relative;
  top: 10px;
  z-index: 2;
  border-radius: 5px;
  font-size: 40px;
  box-shadow: 0 0 5px rgba(0, 0, 0, 0.12), inset 0 0 2px rgba(0, 0, 0, 0.19);
}

.suggestions {
  margin: 0;
  padding: 0;
  position: relative;
  /*perspective: 20px;*/
}

.suggestions li {
  background: white;
  list-style: none;
  border-bottom: 1px solid #D8D8D8;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.14);
  margin: 0;
  padding: 20px;
  transition: background 0.2s;
  display: flex;
  justify-content: space-between;
  text-transform: capitalize;
}

.suggestions li:nth-child(even) {
  transform: perspective(100px) rotateX(3deg) translateY(2px) scale(1.001);
  background: linear-gradient(to bottom,  #ffffff 0%,#EFEFEF 100%);
}

.suggestions li:nth-child(odd) {
  transform: perspective(100px) rotateX(-3deg) translateY(3px);
  background: linear-gradient(to top,  #ffffff 0%,#EFEFEF 100%);
}

span.population {
  font-size: 15px;
}

.hl {
  background: #ffc600;
}

JS - step by step

首先,先來學一下如何抓 Json 檔吧!不知道大家習慣用這種舊式的寫法還是新的 ES6 語法 cities 宣告為 const 並設為空陣列,並再底下將 return 回來的結果 ...拆開 push 回來呢?我是比較喜歡舊的下面這種寫法啦w

Using Fetch:https://ubin.io/cz5PZD

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

  let cities = null
  fetch(endpoint)
    .then(function(blog){
      return blog.json()
    })
    .then(function(myJson){
      return cities = myJson
    })
</script>

瀏覽器 console 看看有沒有抓到:

// cities
// (1000) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, …]

使用者在輸入 City or State 放開按鍵的時候,搜尋與Json的資料有哪幾筆符合?這邊我們必須透過正則RegExp 來寫,此外也來批純的方法xd

<script>
	// 略
  let cities = null
  fetch(endpoint)
    .then(function(blog){
      return blog.json()
    })
    .then(function(myJson){
      return cities = myJson
    })

    document.querySelector('.search').addEventListener("keyup", keyupHandler)
    function keyupHandler(){

    }
</script>

在 pure function findMatches 中,wordToMatch 與 cities 兩個值不會變動,透過正則將 wordToMatch 丟進來,後面 'gi' 裡的 g 代表全部的意思、i 代表不分大小寫,再回資料結構中有 city || state 回傳值...

RegExp:https://ubin.io/pjvYgJ
Pure function:https://ubin.io/RpbKDT

<script>
	// 上略
  document.querySelector('.search').addEventListener("keyup", keyupHandler)
  
  function keyupHandler(){
    const matchArray = findMatches(this.value, cities)
    console.log(matchArray)
  }
  function findMatches(wordToMatch, cities) {
  return cities.filter(function(place){
    const regex = new RegExp(wordToMatch, 'gi')
    return place.city.match(regex) || place.state.match(regex)
  })
}
</script>

接著來處理 DOM 的資料,並新增抓到的資料透過來新增HTML 到 Class .suggestions 裡,這邊單純只是做資料的處理,可能會看不懂的地方是,以前用單引號表示字串在 ES6 中換成蝌蚪按鍵的一點符號(這個該叫什麼好呢xd ),另一個為 numberWithCommas 方法中的 toLocaleString() ,在原作者中是用正則去做處理,不過感覺用這個會比較直接...

Number.prototype.toLocaleString():https://reurl.cc/NaXNde

<script>	
  // 上略
  function keyupHandler(){
    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="population">${numberWithCommas(place.population)}</span>
      </li>
    `
    }).join('')
    document.querySelector('.suggestions').innerHTML = html
  }

  function numberWithCommas(x) {
    return x.toLocaleString()
  }
</script>

就大功告成啦!

JS - Final

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

  let cities = null
  fetch(endpoint)
    .then(function(blog){
      return blog.json()
    })
    .then(function(myJson){
      return cities = myJson
    })

    document.querySelector('.search').addEventListener("keyup", keyupHandler)

    function keyupHandler(){
      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="population">${numberWithCommas(place.population)}</span>
        </li>
      `
      }).join('')
      document.querySelector('.suggestions').innerHTML = html
    }

    function numberWithCommas(x) {
      return x.toLocaleString()
    }

    function findMatches(wordToMatch, cities) {
    return cities.filter(function(place){
      const regex = new RegExp(wordToMatch, 'gi')
      return place.city.match(regex) || place.state.match(regex)
    })
  }
</script>

本刊同步於個人網站:http://chestertang.site/

本次範例程式碼原作者來源:https://reurl.cc/yynKGl


上一篇
新手村05 - Flex Panel Gallery
下一篇
新手村07 - Array Cardio Day 2
系列文
新手村-30 Day JS Coding Challenge30

尚未有邦友留言

立即登入留言