仿百度搜索时的下拉列表(jQuery实现)
当我们使用百度或是谷歌进行搜索时,只要输入一个字母就可以产生一个下拉列表,并且可以通过键盘上下键来控制列表项的选择。最近在项目中也碰到了类似的情况,在文本框中输入某个字母就可以出现一个下拉列表,展现从数据库里搜索过来的结果。
先说一下我的实现思路吧,当用户在文本框中输入时,可以利用ajax方式将文本框内内容传给后台的某个页面,在那个页面中经过处理后将产生的结果(这里的结果是一个字符串数组)再获取过来在前台呈现。有了思路后就开始考虑怎么去具体实现吧。首先要使用ajax的话,就想到可以使用jQuery提供的$.post()方法,这个方法可带4个参数,分别是:
·url,请求的页面URL地址;
·data(可选),作为HTTP消息的实体内容发送给服务器;
·callback(可选),载入成功后的回调函数,自动将请求结果和状态传递给该方法;
·type(可选),服务器端返回的数据格式,可以是:xml、html、script、json等。
好,现在对$.post()方法有了一定的了解,现在就可以考虑这个方法该怎么来写了。在我的项目中,请求的URL地址是@Url.Action("GetTags")(由于是ASP.NET MVC架构,地址是由Routing配置产生的,这个转义到前台就是一个普通的URL地址),需要传送的是ID为newTag的文本框中的值,所以第二个参数为{pinyins: $("#newTag").val()},这里的pinyins是由后台定义的。第三个参数callback是处理请求成功后要做的事,暂且命名为pinyinCallback,第四个参数设为'json',即数据格式为一个JSON对象。这样的话,这个ajax请求可以写为:
$.post('@Url.Action("GetTags")', { pinyins: $("#newTag").val() }, pinyinCallback, 'json');
但是如果用户每输入一个字母就立即往服务器传的的话,服务器的承载就会过大,于是考虑可以将每次请求延迟一秒发送,于是发送请求的方法可以这样定义:
1 function pinyinOption() {
2 var t = setTimeout(function () {
3 $.post('@Url.Action("GetTags")', { pinyins: $("#newTag").val() }, pinyinCallback, 'json');
4 }, 1000);
5
6 }
现在要考虑的就是这个方法在何时调用。既然是以用户输入结果来查询的,那么可以考虑jQuery的keyup事件,可以在文本框(id为newTag)的keyup事件触发后就调用之前定义的方法。但是又考虑到这个功能中可以通过键盘中上下键来控制列表中的选项,所以这个keyup事件可以这样来定义:
1 $("#newTag").keyup(function (e) {
2 if (e.keyCode != 40 && e.keyCode != 38) {
3 currentTxt = $("#newTag").val();
4 pinyinOption();
5 }
6 }).focus(function () {
7 this.select();
8 });
其中keyCode值38、40分别代表上键和下键。currentTxt记录的是当前文本框的内容。
当请求成功后的回调函数pinyinCallback该做什么呢?这个回调函数该做的应该有这些事,首先要把请求来的数据在前台呈现出来,这样就需要创建一些新的DOM元素来装这些内容。就不废话了,直接上代码吧:
Html代码:
1 <div class="mb20 pr f14 ff" id="tagDiv">
2 <p><span>新标签</span> @Html.TextBox("newTag", null, new { style = "border: 1px solid #555;height: 20px;", autocomplete ="off"})
3 <input type="submit" value="添加" /><span>@ViewBag.Info</span></p>
4 </div>
js代码:
1 var $resultDiv = $('<div id="resultDiv" class="pa f12 ff"></div>');
2 var $resultUl = $('<ul id="resultUl"></ul>');
3 var $resultLi = [];
4 var currentTxt;
5
6 function pinyinCallback(data) {
7 $("#resultDiv").show();
8 $resultUl.html("");
9 for (var i = 0; data[i]; i++) {
10 $resultLi[i] = $("<li></li>");
11 $resultLi[i].html(data[i]);
12 $resultUl.append($resultLi[i]);
13 }
14 if ($resultUl.html() == "") {
15 $("#resultDiv").hide();
16 }
17 $resultUl.appendTo($resultDiv);
18 $resultDiv.appendTo($("#tagDiv"));
19 }
目前这个回调函数只是将请求回来的数据添加到创建的id为resultDiv的容器内,再将这个容器添加到页面中已存在的id为tagDiv的容器中。而为了美观一点并为了实现点击一个列表项就提交的功能,这个回调函数还可以做这些工作:
js代码:
1 $("#resultDiv li").hover(function () {
2 $(this).addClass("esultDivLiHover");
3 }, function () {
4 $(this).removeClass("esultDivLiHover");
5 });
6 $("#resultDiv").blur(function () {
7 $("#resultDiv").hide();
8 });
9 $("#resultDiv li").click(function (event) {
10 $("#newTag").val($(this).text());
11 $("form").submit();
12 });
css代码:
1 #resultDiv
2 {
3 left: 47px;
4 border: 1px solid #000;
5 background: #fff;
6 z-index: 100;
7 width: 200px;
8
9 }
10 #resultDiv li
11 {
12 cursor: default;
13 padding: 2px 4px;
14 }
15 .esultDivLiHover
16 {
17 background: #cfcfcf;
18 }
在列表项被点击后就提交一次表单,并把当前文本框的内容设置为列表项的内容。
接下来就要实现键盘上下键控制的功能了,这样,全部的回调函数代码为:
1 function pinyinCallback(data) {
2 $("#resultDiv").show();
3 $resultUl.html("");
4 for (var i = 0; data[i]; i++) {
5 $resultLi[i] = $("<li></li>");
6 $resultLi[i].html(data[i]);
7 $resultUl.append($resultLi[i]);
8 }
9 if ($resultUl.html() == "") {
10 $("#resultDiv").hide();
11 }
12 $resultUl.appendTo($resultDiv);
13 $resultDiv.appendTo($("#tagDiv"));
14 $("#resultDiv li").hover(function () {
15 $(this).addClass("esultDivLiHover");
16 }, function () {
17 $(this).removeClass("esultDivLiHover");
18 });
19 $("#resultDiv").blur(function () {
20 $("#resultDiv").hide();
21 });
22 $("#resultDiv li").click(function (event) {
23 $("#newTag").val($(this).text());
24 $("form").submit();
25 });
26 //键盘上下键控制
27 var index = -1; //标记上下键时所处位置
28
29 document.documentElement.onkeydown = function (e) {
30 e = window.event || e;
31 if (e.keyCode == 40) { //下键操作
32 if (++index == $("#resultDiv li").length) { //判断加一操作后index值是否超出列表数目界限
33 index = -1; //超出的话就将index值变为初始值
34 $("#newTag").val(currentTxt); //并将文本框中值设为用户用于搜索的值
35 $("#resultDiv li").removeClass("esultDivLiHover");
36 }
37 else {
38 $("#newTag").val($($("#resultDiv li")[index]).text());
39 $($("#resultDiv li")[index]).siblings().removeClass("esultDivLiHover").end().addClass("esultDivLiHover");
40 }
41 }
42 if (e.keyCode == 38) { //上键操作
43 if (--index == -1) { //判断自减一后是否已移到文本框
44 $("#newTag").val(currentTxt);
45 $("#resultDiv li").removeClass("esultDivLiHover");
46 }
47 else if (index == -2) { //判断index值是否超出列表数目界限
48 index = $("#resultDiv li").length - 1;
49 $("#newTag").val($($("#resultDiv li")[index]).text());
50 $($("#resultDiv li")[index]).siblings().removeClass("esultDivLiHover").end().addClass("esultDivLiHover");
51 }
52 else {
53 $("#newTag").val($($("#resultDiv li")[index]).text());
54 $($("#resultDiv li")[index]).siblings().removeClass("esultDivLiHover").end().addClass("esultDivLiHover");
55 }
56 }
57 };
58 }
OK,现在这个功能就差不多搞定了,在ie6下也是完全正常,不过还是有一些bug需要改进,另外这个方法还是有可以优化的地方吧。晒一晒效果图:
希望明天又是美好的一天,又不正的地方还希望多多指出。