JSONP调用百度联想搜索API

很久以前就写好的一个小demo,最近又拿出来整理、修改了一下

找到百度的API

首先得找到百度的API,我们假定去百度搜索我的名字Alkali(这不叫阿卡丽!不信看这里),然后得到如下页面

可以在控制台的Network里看到这么一个信息

从中提取出请求地址,掐头去尾缩减掉我们不需要的参数,这样我们就得到以下API:

https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd=Alkali&cb=jQuery1102039187157806009054_1457413554466

这里的wd就是我们请求的关键字,cb即回调函数。

调用API实现联想搜索

首先先讲一下JSONP的原理,即本地动态的创建<script>标签,地址指向第三方API,并允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。

我们再来看看刚刚搜的Alkali的preview

可以看到我们所需数据存放在json.s

根据以上原理,我们可以先写个雏形

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
input.addEventListener('keyup', function(){
ul.innerHTML = '';//清空上一次请求所插入的li
var scripts = document.querySelectorAll('script');
if(scripts.length >= 1){
body.removeChild(scripts[0]);//每次更新新的script之前,移除旧的标签
}
var script = document.createElement('script');
script.src = 'https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd='+ input.value +'&cb=insert';
body.appendChild(script);
}, false);

function insert (json) {
//依次插入json中的数据
for (var i = 0; i < json.s.length; i++) {
var li = document.createElement('li');
li.innerHTML = json['s'][i];
ul.appendChild(li);
}
}

上面的代码已经可以实现了联想搜索,不过还有一定的瑕疵。可以看到我使用的是循环插入dom,这样在需要大量操作dom时,性能上就会不尽人意。不过这里为了方便我就先这么写了→_→。可以使用innerHtml的方法来添加:

1
2
3
4
5
var htmlText = '';
for(var i = 0; i < json.s.length; i++){
htmlText += '<li>' + json['s'][i] + '</li>';
}
ul.innerHTML = htmlText;

除此之外我们还需要针对中文将input里的值在请求之前进行转义。

1
var val = encodeURI(input.value);//将value进行转义之后再进行请求

这里使用的encodeURI()是Javascript中真正用来对URL编码的函数。使用方法大致如下:

1
2
encodeURI('我');//"%E6%88%91"
decodeURI('%E6%88%91');//"我"

再有,我们绑定的是input框的 keyup 事件,但是有很多keyup触发的时候我们并不希望或者并不需要触发insert函数,所以我们可以对input的value值进行对比,如果值没有发生改变则不必进行之后的一系列操作。

1
2
3
4
5
6
7
8
9
10
11
var oldVal;//定义一个存放旧value的变量
input.addEventListener('keyup', function(){
var val = encodeURI(input.value);//将value进行转义之后再进行请求
if(val == oldVal) return;//如果新旧value相等则返回
ul.innerHTML = '';

...

body.appendChild(script);
oldVal = val;//标签添加结束之后再把获取到的值存进oldVal里,等待下一次比较
}, false);

想要简单点的话也可以绑定 input 事件:

1
2
3
4
5
6
7
8
input.addEventListener('input', function(){
var val = encodeURI(input.value);//将value进行转义之后再进行请求
ul.innerHTML = '';

...

body.appendChild(script);
}, false);

为了使用方便,封装成函数是最好不过的选择:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function jsonp(objects){
objects = objects || {};
if(!objects.url || !objects.callback){
throw new Error('参数不合法');
}
//创建script标签并插入
var callbackName = ('jsonp_' + Math.random()).replace(".", "");//随机生成callbackName

var script = document.createElement('script');
var body = document.getElementsByTagName('body')[0];
body.appendChild(script);

//将回调写为window的方法
window[callbackName] = function (json) {
body.removeChild(script);
clearTimeout(script.timer);
window[callbackName] = null;
objects.callback && objects.callback(json);
};

//发出请求
script.src = objects.url + callbackName;

//响应时间
if(objects.time){
script.timer = setTimeout(function () {
window[callbackName] = null;
body.removeChild(script);
objects.fail && objects.fail('超时');
}, objects.time);
}
}

下面是我最后使用的js代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
;(function(){
window.onload = function(){
var input = document.getElementById('input'),
body = document.getElementsByTagName('body')[0],
ul = document.getElementById('ul');
input.addEventListener('input', function(){
var val = encodeURI(input.value);
ul.innerHTML = '';//清空上一次请求所插入的li
jsonp({
url: 'https://sp0.baidu.com/5a1Fazu8AA54nxGko9WTAnF6hhy/su?wd='+ val + '&cb=',
time: 3000,
callback: function(json){
var htmlText = '';
for(var i = 0; i < json.s.length; i++){
htmlText += '<li>' + json['s'][i] + '</li>';
}
ul.innerHTML = htmlText;
},
fail: function (mes) {
alert(mes);
}
});
}, false);

function jsonp(objects){
objects = objects || {};
if(!objects.url || !objects.callback){
throw new Error('参数不合法');
}

//创建script标签并插入
var callbackName = ('jsonp_' + Math.random()).replace(".", "");//随机生成callbackName

var script = document.createElement('script');
var body = document.getElementsByTagName('body')[0];
body.appendChild(script);

window[callbackName] = function (json) {
body.removeChild(script);
clearTimeout(script.timer);
window[callbackName] = null;
objects.callback && objects.callback(json);
};

//发出请求
script.src = objects.url + callbackName;

//响应时间
if(objects.time){
script.timer = setTimeout(function () {
window[callbackName] = null;
body.removeChild(script);
objects.fail && objects.fail('超时');
}, objects.time);
}
}
}
})(window);

注意上面的window[callbackName] = function(){};因为我用立即执行函数包裹住了所有的代码,使得回调函数没有暴露在全局作用域里,而jsonp只在window下调用callback函数,所以需要将jsonp函数里的回调函数写为window的方法,大概就是这样😑

说了这么多,是时候放出没有任何css样式的demo了,外貌协会请轻喷。。