JavaScript 30天编码挑战——第 6 天

281 阅读3分钟

这是我参与更文挑战的第8天,活动详情查看: 更文挑战

Ajax Type Ahead

一、效果展示

今天这个案例严格来说没有什么技巧而言,他就是一个实操的案例。使用者在输入框输入一个词,迅速匹配包含这个关键词的城市名(用的不是非常舒服,我看有人换成了中文诗句,我准备下次也换这个搞搞,不今天先实现效果,之后在进行换源)。

1.index-START.html

4.gif

2.index-FINISHED.html

1.gif

3.我的效果

2.gif

二、实现

最终代码

<script>
  const endpoint = 'https://gist.githubusercontent.com/Miserlou/c5cd8364bf9b2420bb29/raw/2bf258763cdddd704f8ffd3ea9a3e81d25e2c6f6/cities.json';
  
  // function requestHandler(){
  //   console.log(JSON.parse(this.response));
  // }

  // let req = new XMLHttpRequest();

  // req.addEventListener('load',requestHandler);
  // req.open('get',endpoint);
  // req.send();

  let cities = null;

  fetch(endpoint)
  .then(blob => blob.json())
  .then(data => (cities = data ));

  function findMatches(wordToMatch, cities) {
  return cities.filter(place => {
    // here we need to figure out if the city or state matches what was searched
    const regex = new RegExp(wordToMatch, 'gi');
    return place.city.match(regex) || place.state.match(regex)
  });
}

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

  function inputHandler(){
    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('');
  suggestions.innerHTML = html;
  }

  document.querySelector('.search').addEventListener('keyup', inputHandler);
  const suggestions = document.querySelector('.suggestions');

    
</script>

三、总结回顾

过程分解

  1. 先做一个 request请求。

(1)首先我们根据原作者给的接口地址做一个传统的 HTTP 请求,使用的是 XMLHttprequest。

const endpoint = 'https://gist.githubusercontent.com/Miserlou/c5cd8364bf9b2420bb29/raw/2bf258763cdddd704f8ffd3ea9a3e81d25e2c6f6/cities.json';
  
  function requestHandler(){
    console.log(JSON.parse(this.response));
  }

  let req = new XMLHttpRequest();

  req.addEventListener('load',requestHandler);
  req.open('get',endpoint);
  req.send();

然后这样的话,我们就实现了一个传统的 HTTP request,可以看到请求回来的数据:

3.gif

(2)然后再用 fetch来做一个请求,得到的是同样的结果。

fetch(endpoint)
  .then(blob => blob.json())
  .then(data => console.log(data));

5.gif

这里先不做 这两种的区别,我们将这个环节放到后边去解释。

  1. 做事件绑定。 我们绑定搜索框的 keyup 事件,因为要立刻得到结果,所以我们这里就使用了 keyup 事件。

一开始我用了 change 事件,发现并不能满足要求,所以使用 keyup 才是真确的选择。

document.querySelector('.search').addEventListener('keyup', inputHandler);
  1. 正则匹配。 既然我们有了事件去返回结果,那么如果让我们的输入和返回结果进行匹配呢,这里就用到了正则表达式,我们这里就直接使用原作者的就行(因为我太菜了),
function findMatches(wordToMatch, cities) {
  return cities.filter(place => {
    // here we need to figure out if the city or state matches what was searched
    const regex = new RegExp(wordToMatch, 'gi');
    return place.city.match(regex) || place.state.match(regex)
  });
}
function inputHandler(){
    const matchArray = findMatches(this.value,cities);
    console.log(matchArray);
  }

这里匹配的是返回值里边的 city 和 state 。 我们输出查看一下结果:

6.gif

7.gif 从这张图里我们可以看到,当我输入 N 的时候,返回了 835 条数据,当我再次追加了一个字母 a 的时候,返回了 131 条数据,当我再追加一个字母 s 的时候,这时候结果值只有 4 条,

image.png

  1. 匹配数据的显示。 既然我们已经做到了匹配数据返回,剩下的就是在页面上显示了,为了和原作者的效果保持统一,我们这里可以直接将原作者的代码拿过来做一个解析。

这里边又用到了 ES6 的模板字符串,相对来说对于双引号和单引号来说,省了我们很多的事情。

其次这里用到了 replace() 方法,这个方法可以匹配正则的规则,我们可以进行操作一下:

image.png 上面图里第一个 repalce 返回的结果里边,只有第一个 e 被替换了,而用了正则的话,底下的 replace 就会替换掉 所有的 e ,匹配到了所有的 e 进行了替换。

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

  function inputHandler(){
    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('');
  suggestions.innerHTML = html;
  }

  document.querySelector('.search').addEventListener('keyup', inputHandler);
  const suggestions = document.querySelector('.suggestions');

这里边有一个小细节,里边的 numberWidthCommas 事件将返回值的 population 字段进行了千分位的显示。

8.gif

四、重难点

fetch 和 XMLHttprequest

如果要是说今天的重难点的话,我可能要说的就是 fetch 和 XMLHttprequest 的区别了。

关于 fetch 和 XMLHttprequest 的相关了解可以这里传送门进行学习了解:
XMLHttprequest : developer.mozilla.org/zh-CN/docs/…
fetch : developer.mozilla.org/zh-CN/docs/…

对于前端来说,Axios应该不陌生,自从尤大推荐后,Axios几乎成了前端必备工具库,Axios的体积也与日俱增,当前最新版本已经达到了14k的size,这样的大小,在sdk中引用是不太合适的,而XMLHttpRequest又过于原始,还不支持promise,需要进一步封装。那么有没有简单便捷的ajax API呢?它就是Fetch。

大家可能日常接触的比较多的就是 XMLHttprequest 了,由于时间关系,那么我们就简单来说一下 fetch 吧,之后会专门对 fetch 去做一个解析(毕竟我也对这个不是很了解...手动狗头)。

fetch 的语法是这样的:

fetch(input, init).then(function(response) { ... });

MDN 也提供了一个基本的 fetch 发送请求:

    var myImage = document.querySelector('img');
    
    fetch('flowers.jpg')
    .then(function(response) {
      return response.blob();
    })
    .then(function(myBlob) {
      var objectURL = URL.createObjectURL(myBlob);
      myImage.src = objectURL;
    });

这个示例里边用到了 blob() 方法, 这个方法在这里用来获取图片的内容,这是 Body 类定义的一个方法。

我们再看一下 fetch 的浏览器兼容性:

image.png

Fetch 是 web异步通信的未来. 从chrome42, Firefox39, Opera29, EdgeHTML14(并非Edge版本)起, fetch就已经被支持了。

那么提出一个问题,如果说现在只能用这两种的话,大家会使用哪种呢,可能有的人会说我选择 fetch ,很明显从代码量上来看,fetch 用起来更为方便。确实如此,但是我还是会选择比较传统一点的,第一是因为用习惯了,第二的话就是一个老的东西,他能这么久还没有被淘汰,说明他是有存在的必要的,也有着一些不可撼动的地位。

关于 fetch :

fetch基于Promise, Promise受限, fetch也难幸免. ES6的Promise基于 Promises/A+ 规范 , 它只提供极简的api, 没有 timeout 机制, 没有 progress 提示, 没有 deferred 处理 (这个可以被async/await替代)。除此之外, fetch还不支持jsonp请求。当然这些肯定也有一些解决办法的,我们这里不多过于赘述,先将一些资料罗列,后边我们进行详细的解析。

正则表达式

MDN地址:developer.mozilla.org/zh-CN/docs/…

说实话,我并没有掌握这个东西,我都是现用现查,大家可以自行上网学习,之前有个老师推过一本正则mini小书(JavaScript正则表达式迷你书),我大体翻看了一下,觉得不错,下面给大家贴一下链接。   链接:pan.baidu.com/s/1_KIOUVDy…  提取码:69wm