Google Suggest Autcomplete 的实现
前台:
HTML:
Default.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Using Google Search</title>
<script type="text/JavaScript" src="fun.js">
</script>
</head>
<body>
<h1>Google Suggest</h1>
<input type="text" id="tb_kw" name="textField" onkeyup="connectGoogleSuggest()" style="width: 270px"/>
<div id="targetDiv" style="position:absolute;border:solid 1px black; z-index:20000; background-color:White; display:none;"></div>
</body>
</html>
func.js:
function $(i) { return document.getElementById(i);}
function getPosition(ele){
var overflown = [];
var el = ele, left = 0, top = 0;
do {
left += el.offsetLeft || 0;
top += el.offsetTop || 0;
el = el.offsetParent;
} while (el);
return {'x': left, 'y': top};
}
function connectGoogleSuggest() {
var input = $("tb_kw");
var h=$("targetDiv");
//h._i=-1000;
if (!input.value||!input.value.length||event.keyCode=='27')
{
h.style.display="none";
return;
}
if (event.keyCode=='38' || event.keyCode=='40')
{
// alert(h.style.display);
if (h.style.display=="none")
{
return;
}
if (event.keyCode=='38')
{
//alert(event.keyCode);
if (h._i==-1)
{
h._i=h.firstChild.rows.length-1;
}
else
{
h._i--;
}
}
else
{
//alert(h._i);
h._i++;
}
for(var i=0; i<h.firstChild.rows.length; i++)
{
h.firstChild.rows[i].style.backgroundColor="#FFFFFF";
}
if(h._i >= 0 && h._i < h.firstChild.rows.length)
with(h.firstChild.rows[h._i])
{
style.backgroundColor="#D0ECF9";
input.value=cells[0].innerText;
}
else
{
input.value=h._kw;
h._i=-1;
}
}
else if(input.value!="") {
h._i=-1;
h._kw=input.value;
getData("google.aspx?qu="+input.value);
var pos=getPosition($("tb_kw"));
with(h.style)
{
left=pos.x;
top=pos.y+$("tb_kw").offsetHeight;
width=$("tb_kw").offsetWidth-4;
display="block";
}
}
else {
$("targetDiv").innerHTML= "";
}
}
function getData(source){
var xmlHttp = false;
if (window.XMLHttpRequest)
xmlHttp = new XMLHttpRequest();
else if (window.ActiveXObject)
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
if (xmlHttp){
xmlHttp.open("GET",source);
xmlHttp.onreadystatechange=function(){
if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
var message=xmlHttp.responseText;
var callBack = message.substring("window.google.ac.".length, message.length);
eval(callBack);
}
}
xmlHttp.send(null);
}
}
function Suggest_apply(unusedVariable, searchWord, results,unusedArray){
if(!results || results.length<3)
return;
var ihtml="";
for(var j=1;j<results.length;j+=2)
ihtml+='<tr style="cursor:hand" onmouseover="MouseOver(this);$(\'tb_kw\').value=\'' +results[j] +'\';" onmouseout="MouseOut(this);" onclick="ResultOnClick();"><td style="color:#000" align="left">' +results[j] +'</td><td style="color:#090" align="right">' +results[j+1] +'</td></tr>';
$("targetDiv").innerHTML="<table width='100%' border='0' cellpadding='0' cellspacing='0' style='font-size:12px;'>"+ihtml+"</table>";
$("targetDiv").style.display="block";
}
function MouseOver(Tr){
Tr.bgColor="#D0ECF9";
Tr.childNodes[0].style.color="#00cc00";
Tr.childNodes[1].style.color="#00cc00";
}
function MouseOut(Tr){
Tr.bgColor="";
Tr.childNodes[0].style.color="#000000";
Tr.childNodes[1].style.color="#009900";
}
function ResultOnClick()
{
$("targetDiv").style.display="none";
}
后台:
google.aspx
<%@ Page Language="C#" AutoEventWireup="true" Debug="true" CodeFile="google.aspx.cs" Inherits="google" %>
记住:一定要只保留这一句话,其他的就去掉
google.aspx.cs
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Text;
using System.IO;
using System.Net;
public partial class google : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string ret = GetPageHtml("http://www.google.cn/complete/search?hl=zh-CN&client=suggest&js=true&qu=" + Request.QueryString["qu"]);
Response.Write(ret);
}
protected string GetPageHtml(string url)
{
string pageinfo;
try
{
WebRequest myreq = WebRequest.Create(url);
WebResponse myrep = myreq.GetResponse();
StreamReader reader = new StreamReader(myrep.GetResponseStream(), Encoding.GetEncoding("UTF-8"));
pageinfo = reader.ReadToEnd();
}
catch
{
pageinfo = "";
}
return pageinfo;
}
}
上面把代码全部贴出来了,下面我就简单讲解一下。
我们用的是Google Suggest的API,即这个页面
http://www.google.cn/complete/search?hl=zh-CN&client=suggest&js=true&qu=" + Request.QueryString["qu"])
这里我们将我们的搜索词作为QueryString传了过去,在google.aspx.cs页面进行分析
如下:
string ret = GetPageHtml("http://www.google.cn/complete/search?hl=zh-CN&client=suggest&js=true&qu=" + Request.QueryString["qu"]);
Response.Write(ret);
GetPageHtml()方法源码如下:
protected string GetPageHtml(string url)
{
string pageinfo;
try
{
WebRequest myreq = WebRequest.Create(url);
WebResponse myrep = myreq.GetResponse();
StreamReader reader = new StreamReader(myrep.GetResponseStream(), Encoding.GetEncoding("UTF-8"));
pageinfo = reader.ReadToEnd();
}
catch
{
pageinfo = "";
}
return pageinfo;
}
}
GetPageHtml()的主要功能就是获得请求的web页的内容。
Ret就是我们要的结果,就是我们回调函数的返回结果,返回一个数组,比如搜索词为AJAX,则有
window.google.ac.Suggest_apply(frameElement, "AJAX", new Array(2 , "ajax技术", "2,400,000 结果" , "ajax基础教程", "444,000 结果" , "ajax 框架", "860,000 结果" , "ajax教程", "1,210,000 结果" , "ajaxcontroltoolkit", "297,000 结果" , "ajaxpro", "89,200 结果" , "ajax .net", "2,060,000 结果" , "ajax中国", "2,550,000 结果" , "ajax 实例", "884,000 结果" , "ajax post", "18,700,000 结果" ), new Array(""));
所以我们得出结论:回调函数是window.google.ac.Suggest_apply(a, b, c, d), c就是我们要的结果,所以我们在回调函数中只需要使用c这个数组就得了。
另外就是AJAX的套路,xmlHttp请求,这里我们用一个函数function getData(source)实现
function getData(source){
var xmlHttp = false;
if (window.XMLHttpRequest)
xmlHttp = new XMLHttpRequest();
else if (window.ActiveXObject)
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
if (xmlHttp){
xmlHttp.open("GET",source);
xmlHttp.onreadystatechange=function(){
if (xmlHttp.readyState == 4 && xmlHttp.status == 200) {
var message=xmlHttp.responseText;
var callBack = message.substring("window.google.ac.".length, message.length);
eval(callBack);
}
}
xmlHttp.send(null);
}
}
为什么我们要在google.aspx.cs中进行处理呢?原因是这样的,大多数的浏览器都不支持跨域的JS访问,从而我们要从后台进行处理后再请求,就不是跨域访问了。
接下来解决的问题是我们的autocomplete的结果接收的DIV的位置了,位置可以这样实现:
function getPosition(ele){
var overflown = [];
var el = ele, left = 0, top = 0;
do {
left += el.offsetLeft || 0;
top += el.offsetTop || 0;
el = el.offsetParent;
} while (el);
return {'x': left, 'y': top};
}
参数ele代表的是文本框,所以文本框在哪里,结果接收DIV就相应的在该在的地方出现,以下为效果图:
接下来是实现回调函数
function Suggest_apply(unusedVariable, searchWord, results,unusedArray){
if(!results || results.length<3)
return;
var ihtml="";
for(var j=1;j<results.length;j+=2)
ihtml+='<tr style="cursor:hand" onmouseover="MouseOver(this);$(\'tb_kw\').value=\'' +results[j] +'\';" onmouseout="MouseOut(this);" onclick="ResultOnClick();"><td style="color:#000" align="left">' +results[j] +'</td><td style="color:#090" align="right">' +results[j+1] +'</td></tr>';
$("targetDiv").innerHTML="<table width='100%' border='0' cellpadding='0' cellspacing='0' style='font-size:12px;'>"+ihtml+"</table>";
$("targetDiv").style.display="block";
}
$(i)是函数function $(i) { return document.getElementById(i);}
主要是为了节省codes。我们实际上是生成了一个<table></table>,里面就是我回调的结果,我们把鼠标时间一通加在了拼接的字符串里面,就是鼠标放到某一个结果上之后,字体颜色改变,背景色改变,然后该项的关键字成为搜索框里面的内容。
一下就是鼠标触发事件函数
function MouseOver(Tr){
Tr.bgColor="#D0ECF9";
Tr.childNodes[0].style.color="#00cc00";
Tr.childNodes[1].style.color="#00cc00";
}
function MouseOut(Tr){
Tr.bgColor="";
Tr.childNodes[0].style.color="#000000";
Tr.childNodes[1].style.color="#009900";
}
function ResultOnClick()
{
$("targetDiv").style.display="none";
}
有鼠标移到结果上,鼠标移开,鼠标点击等。
接下来就是事件函数的主体及其键盘操作
function connectGoogleSuggest() {
var input = $("tb_kw");
var h=$("targetDiv");
//h._i=-1000;
if (!input.value||!input.value.length||event.keyCode=='27')
{
h.style.display="none";
return;
}
if (event.keyCode=='38' || event.keyCode=='40')
{
// alert(h.style.display);
if (h.style.display=="none")
{
return;
}
if (event.keyCode=='38')
{
//alert(event.keyCode);
if (h._i==-1)
{
h._i=h.firstChild.rows.length-1;
}
else
{
h._i--;
}
}
else
{
//alert(h._i);
h._i++;
}
for(var i=0; i<h.firstChild.rows.length; i++)
{
h.firstChild.rows[i].style.backgroundColor="#FFFFFF";
}
if(h._i >= 0 && h._i < h.firstChild.rows.length)
with(h.firstChild.rows[h._i])
{
style.backgroundColor="#D0ECF9";
input.value=cells[0].innerText;
}
else
{
input.value=h._kw;
h._i=-1;
}
}
else if(input.value!="") {
h._i=-1;
h._kw=input.value;
getData("google.aspx?qu="+input.value);
var pos=getPosition($("tb_kw"));
with(h.style)
{
left=pos.x;
top=pos.y+$("tb_kw").offsetHeight;
width=$("tb_kw").offsetWidth-4;
display="block";
}
}
else {
$("targetDiv").innerHTML= "";
}
}
记住,js很宽松,当你给一个对象随便赋值一个属性时,该属性就是该对象的属性了,比如本来objA没有pro1属性,但是当执行运算objA.pro1后,pro1就是它的属性了。基于上述观点,我们有里面的h._i出现,不然一般人还看半天还摸不着头。
主要处理了上下键的操作和控制接收结果的DIV“targetDiv”的某一些样式。然后把这个主要事件函数作为输入框的onkeyup触发事件,当每发生一次按键事件时,便执行一次。实现了实时改变结果的功能。
一个地道的Google Suggest就做好了,大家鼓掌!
马劲 (lkmmmj@gmail.com )2008-11-30 于华中科技大学东七楼607