这是我参与更文挑战的第8天,活动详情查看: 更文挑战
Ajax Type Ahead
一、效果展示
今天这个案例严格来说没有什么技巧而言,他就是一个实操的案例。使用者在输入框输入一个词,迅速匹配包含这个关键词的城市名(用的不是非常舒服,我看有人换成了中文诗句,我准备下次也换这个搞搞,不今天先实现效果,之后在进行换源)。
1.index-START.html
2.index-FINISHED.html
3.我的效果
二、实现
最终代码
<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>
三、总结回顾
过程分解
- 先做一个 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,可以看到请求回来的数据:
(2)然后再用 fetch来做一个请求,得到的是同样的结果。
fetch(endpoint)
.then(blob => blob.json())
.then(data => console.log(data));
这里先不做 这两种的区别,我们将这个环节放到后边去解释。
- 做事件绑定。 我们绑定搜索框的 keyup 事件,因为要立刻得到结果,所以我们这里就使用了 keyup 事件。
一开始我用了 change 事件,发现并不能满足要求,所以使用 keyup 才是真确的选择。
document.querySelector('.search').addEventListener('keyup', inputHandler);
- 正则匹配。 既然我们有了事件去返回结果,那么如果让我们的输入和返回结果进行匹配呢,这里就用到了正则表达式,我们这里就直接使用原作者的就行(因为我太菜了),
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 。 我们输出查看一下结果:
从这张图里我们可以看到,当我输入 N 的时候,返回了 835 条数据,当我再次追加了一个字母 a 的时候,返回了 131 条数据,当我再追加一个字母 s 的时候,这时候结果值只有 4 条,
- 匹配数据的显示。 既然我们已经做到了匹配数据返回,剩下的就是在页面上显示了,为了和原作者的效果保持统一,我们这里可以直接将原作者的代码拿过来做一个解析。
这里边又用到了 ES6 的模板字符串,相对来说对于双引号和单引号来说,省了我们很多的事情。
其次这里用到了 replace() 方法,这个方法可以匹配正则的规则,我们可以进行操作一下:
上面图里第一个 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 字段进行了千分位的显示。
四、重难点
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 的浏览器兼容性:
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