前段时间在找一个网址搜索提示的API,也没发现有公开的API。得,公开的没找到,找个没公开的吧。看看hao123、360等网址导航都有自定义网址,在添加网址时都有网址提示,打开浏览器调试工具,查看一下网络请求吧。
先看hao123,假设搜索bokeyuan这几个字母,从网络监视里得到的请求url是http://nssug.baidu.com/su?cb=jQuery172002392077026888728_1403322091978 &prod=superpage &sc=hao123 &wd=bokeyuan &_=1403324515020
把不需要的参数去掉,剩下的url是:http://nssug.baidu.com/su?wd=bokeyuan &prod=superpage。在浏览器中请求一下,返回的内容是window.baidu.sug({q:"bokeyuan", p:false, s:["博客园0{#S+_}[1928,\"http:\\/\\/www.cnblogs.com\\/\",1,1,\"\\u535a\\u5ba2\\u56ed\"]"]});。内容还是比较乱的,具体参数对应的含义也不是很清楚,用起来稳定性也不能得到保证。
再看看360导航,搜索bokeyuan,得到的url是http://suggest.h.qhimg.com/index.php? biz=websitename &word=bokeyuan &fmt=jsonp &cnt=8 &cb=__jsonp14__ &t=2338874
把多余的参数去掉,剩下的url是:http://suggest.h.qhimg.com/index.php? biz=websitename& word=bokeyuan& fmt=json。在浏览器中请求一下,返回的内容是["\u535a\u5ba2\u56ed,www.cnblogs.com"],这个就比hao123的简洁多了,直接返回一个json数组,每项都是被一个逗号隔开的网站名和网址,这个使用起来就方便的多了。
从获取网站图标的方式到网址搜索提示API,可以看出hao123更注重数据的保护,请求流程及其繁琐,外人不宜用。而360导航就感觉比较直白,丝毫不对外隐藏什么,也比较适合外人使用。
下边是用了360导航的网站搜索提示API并仿照360的网址添加样式的一个网站搜索提示框。试用一下,看看还有没有什么问题,存在bug请及时反馈给我。
一. 实现过程
大概地介绍一下它的实现吧。html布局如下,css样式就不说了,可以自己查看一下。添加按钮和回车的事件是写在页面里的,也可以自己查看。
<div id="mysite"> <div class="txt">网站名称:</div> <div class="site-input site-name"> <input type="text" id="mysite-name-add" placeholder="如:Simple" /> </div> <div class="txt txt-url">网址:</div> <div class="site-input site-url"> <input type="text" id="mysite-url-add" placeholder="如:www.haosimple.com" /> </div> <button onclick="add()" id="btn-site-add" >添加</button> </div>
重点说一下提示功能,js提示的功能单独封装在了一个js文件中,注释都比较清楚,看起来比较简单。代码流程如下:
var hoverIndex = -1; var ajaxDelay; $(document) .ready( function() { applyCSSAndEvent(); addSug(); var sugContainer = $(".mysite-suggest"); //监听文本框的按键抬起事件 $("#mysite-name-add,#mysite-url-add") .keyup( function(event) { // 若上次间隔时间未到,先取消上次提交 window.clearTimeout(ajaxDelay); var e = event || window.event; var keyCode = e.keyCode; // 数字字母退格删除空格 if (keyCode >= 48 && keyCode <= 57 || (keyCode >= 65 && keyCode <= 90) || keyCode == 8 || keyCode == 46 || keyCode == 32) { // 获得搜索地址和搜索内容 var word; var sugApiUrl; if ("mysite-name-add" == document.activeElement.id) { word = $("#mysite-name-add") .val(); sugApiUrl = "http://suggest.h.qhimg.com/index.php?biz=websitename&fmt=jsonp&word="; } else { word = $("#mysite-url-add") .val(); sugApiUrl = "http://suggest.h.qhimg.com/index.php?biz=websiteurl&fmt=jsonp&word="; } // 没有内容就没必要搜索了 if (word.trim() == "") { clearSug(); return; } // 500毫秒后进行搜索,500毫秒未到又触发了下次,本次搜索会被取消 ajaxDelay = window.setTimeout( 'getSite("' + word + '","' + sugApiUrl + '")', 400); return; } // 获得提示框中的项 var sitelis = $(sugContainer).children( "li"); // 响应向上键 if (keyCode == 38 && sitelis.length > 0) { if (hoverIndex > 0) { hoverIndex--; } else { hoverIndex = sitelis.length - 1; } setContent($(sitelis)[hoverIndex]); changeHoverClass($(sitelis)[hoverIndex]); } // 响应向下键 if (keyCode == 40 && sitelis.length > 0) { if (hoverIndex < sitelis.length - 1) { hoverIndex++; } else { hoverIndex = 0; } setContent($(sitelis)[hoverIndex]); changeHoverClass($(sitelis)[hoverIndex]); } }); });
第一步,先利用applyCSSAndEvent()函数加载css样式和绑定冒泡事件。样式主要是针对动态生成的提示框及提示项的样式,以这种方式设置css只需要加载一次即可,避免提示项改变重复设置css的做法。然后绑定mouseover事件,当鼠标扫过提示项时,要高亮显示。提示项的mousedown事件响应用户点击时将内容放置到文本框中。还绑定了文本框的键盘向上键事件,因为默认情况下在文本框中按向上键会使光标移到文字前边。如果不这样做的话,在按向上键切换提示项时,光标总会先到文本框前边然后再到文本框后边,体验不好。最后是绑定文本框失去焦点事件,清空并隐藏提示框。
function applyCSSAndEvent() { $(document.body) .append( '<style type="text/css">.mysite-suggest{display:none;position:absolute;border:1px solid #707a86;background-color:#fff}.mysite-suggest li{overflow:hidden;height:30px;cursor:pointer}.mysite-suggest .sug-url,.mysite-suggest .sug-name{display:inline;overflow:hidden;float:left;padding-left:6px;font-size:14px;height:30px;line-height:30px;text-overflow:ellipsis;white-space:nowrap}.mysite-suggest .sug-url{margin-right:12px;width:206px}.mysite-suggest .sug-name{margin-right:80px;width:188px}.mysite-suggest .hover{background-color:#f2f8ff}</style>'); $(document).on("mouseover", ".mysite-suggest li", function() { changeHoverClass(this); }); $(document).on("mousedown", ".mysite-suggest li", function() { setContent(this); }); $(document).on("keydown", "#mysite-name-add,#mysite-url-add", function(e) { if (e.keyCode == 38)// 将文本框向上键的响应取消 return false; }); // 当文本框失去焦点时隐藏提示框 $(document).on("blur", "#mysite-name-add,#mysite-url-add", function() { clearSug(); }); }
// 改变提示项的高亮属性 function changeHoverClass(li) { $(".mysite-suggest .hover").removeClass(); $(li).addClass("hover"); }
// 设置网站名、网址文本框的内容 function setContent(li) { $("#mysite-name-add").val($(li).children(":first-child").text()); $("#mysite-url-add").val($(li).children(":last-child").text()); }
// 清空提示框 function clearSug() { $(".mysite-suggest").html(" "); $(".mysite-suggest").hide(); }
第二步,利用addSug()函数添加提示框,在body最后添加一个ul标签,设置其位置在文本框下边,并使其宽度覆盖两个文本框,代码如下:
function addSug() { var name_container = $(".site-name"); var url_container = $(".site-url"); var name_container_offset = name_container.offset(); var url_container_offset = url_container.offset(); $(document.body).append('<ul class="mysite-suggest"></ul>'); $(".mysite-suggest").css("top",(name_container_offset.top + name_container.height())).css("left", name_container_offset.left).width( url_container_offset.left - name_container_offset.left + url_container.width()); }
最后,监听文本框的键盘键入事件,如果是数字字母空格删除退格按键,则根据在不同文本框键入的内容切换请求地址和搜索内容,因为网站名搜索和url搜索的API是不一样的。然后定义一个超时发送的ajax,超时时间为400ms。同时文本框还响应向上向下按键,根据选中的列表项替换文本框的内容。其中ajax请求的代码如下:
function getSite(word, sugApiUrl) { $.ajax({ type : "get", url : sugApiUrl + word, dataType : "jsonp",// 数据类型为jsonp jsonp : "cb",// 服务端用于接收callback调用的function名的参数,360导航必须是cb jsonpCallback : "callback",// 不能采用默认的函数名,360导航对回调名称有限制 success : function(data) { clearSug();// 先清除上一次的列表 var sugContainer = $(".mysite-suggest"); $(data).each(function() { sugContainer.append(generateSiteList(this)); }); // 当返回的数据长度大于0才显示 if ($(sugContainer).children("li").length > 0) { sugContainer.show(); hoverIndex = -1; } } }); }
由于这是跨域名请求,所以需要用到jsonp请求方式,请求到内容之后,把内容加载到提示框中并显示。生成列表项的代码如下所示,构建了一个li标签,里边有两个div分别显示网站名和网址,其中css样式就是在开始时js加载过的样式。
function generateSiteList(data) { var name = data.split(',')[0]; var url = data.split(',')[1]; return '<li><div class="sug-name">' + name + '</div> <div class="sug-url">' + url + '</div></li>'; }