一、 引言
Web世界中一項廣為使用的功能就是搜索。隨著Web技術的日益發展,為了更好地滿足客戶的需求,常規搜索引擎開始對更多的非常規方式"敞開了大門"。在這方面,Yahoo!率先提供出其Y!Q服務。這個新的服務能夠使你搜索任何web頁面,前提是該頁面的作者必須包括在他們的web頁面中。是服務技術實現了把相關的搜索結果呈現到讀者眼前,從而向讀者展示更多的信息而不必離開他們的當前頁面。
Yahoo!的Y!Q服務的確是一個偉大的想法,但是它的出現也受到了一些批評。原因何在?首先,它要求客戶端必須使用Yahoo!的JavaScript並且你必須添加一個<form/>元素,以便滿足Yahoo!的搜索要求。對於許多網站作者來說,提供該服務要求付出太多的努力。並且,在所有這些條件滿足之後,搜索結果將以Yahoo!風格展現,從而破壞了用戶網站的外觀感覺。
幸運的是,Yahoo!並非唯一的提供"從你的網站提供搜索結果"服務的搜索引擎。MSN Search也提供一種類似服務,除了它能夠使web開發者控制外觀感覺以外。這種能力來自於MSN Search提供它的搜索結果的RSS版本,從而使得訂閱一個特定的搜索或使用AJax方法把該結果添加到你的頁面成為可能。
盡管Google已經率先實現了這種新的"從你的站點進行搜索"技術;但是,在本文成文之時,與Google相關的Google BlogSearch Beta也已經能夠以RSS或者Atom格式提供返回的結果。
二、 服務器端組件 使用MSN Search執行搜索時,你會看到一個桔黃.色的XML圖像出現在結果頁面的底部。點擊這個圖像將會把你帶到一個新的頁面,並提供給你訂閱該搜索的URL。
這樣以來,你就可以編寫服務器端代碼以檢索遠程饋送。對於本文中的搜索窗,你將使用PHP檢索搜索饋送。來自於服務器應用程序請求信息的URL看起來如下所示:
websearch.PHP?search=[SEARCHTERM]
查詢字符串只有一個變量:"search";因此,應用程序應該查找此查詢項。在服務器端,你需要創建一個頁面來"拉動"這個數據:
<?PHP
header("Content-Type: text/XML");
header("Cache-Control: no-cache");if ( isset($_GET["search"]) )
{
$searchTerm = urlencode( stripslashes($_GET["search"]) );
$url = "http://search.msn.com/results.ASPx?q=$searchTerm&format=rss";
$XML = file_get_contents($url);
echo $XML;
}
?>
前兩行設置要求的頭部以便浏覽器能夠正確處理數據(XML形式,並且沒有對結果予以緩沖)。下一行代碼使用isset()函數來決定是否搜索鍵存在於查詢字符串中。
為了把一個合適的請求發送到遠程主機,搜索術語應該通過許多函數的"過濾"。首先,它被傳遞給stripslashes()函數。如果在PHP配置中啟動了"magic quotes"(缺省情況下是支持的),那麼,到達PHP引擎的任何引號都會被使用一個斜槓(如,\"search query\")自動地脫掉。該stripslashes()函數負責刪除這些符號,僅留下"search query"。在刪除斜槓後,轉到urlencode()函數,由這個函數負責編碼字符以用於查詢字符串。空格、引號、"&"等符號都被編碼。
注意 如果搜索術語不能通過這些函數的"過濾",那麼,MSN服務器將返回一個代碼400-"Bad Request"。
當為轉換搜索術語准備好後,它被包括到URL中並存儲於$url變量中。最後,file_get_contents()函數負責打開遠程文件,讀取其內容並以一個字符串形式把它返回到$XML變量中,然後使用echo命令把它打印到頁面中。
三、 客戶端組件 本文中的搜索窗口(Widget)的客戶端代碼基於一個靜態對象msnWebSearch創建-它被定義為一個沒有任何屬性(現在)的對象:
var msnWebSearch ={};
這個對象用於一個HtmlElement的onclick事件中執行搜索:
<a href="#"
onclick='msnWebSearch.search(event,"Professional AJax"); return false;'>
Professional AJax
</a>
這個msnWebSearch對象提供若干取得搜索結果的方法,並負責繪制和放置包含這些數據的HTML。第一個方法是drawResultBox(),它負責繪制HTML。這個方法形成的Html如下所示:
<divclass="AJaxWebSearchBox">
<div class="AJaxWebSearchHeading">MSN Search Results
<a class="AJaxWebSearchCloseLink" href="#">X</a>
</div>
<div class="AJaxWebSearchResults">
<a class="AJaxWebSearchLink" target="_new" />
<a class="AJaxWebSearchLink" target="_new" />
</div>
</div>
該結果框分為兩部分:一個頭部和一個結果欄(見圖1)。頭部告訴用戶這個新的搜索窗包含來自一個MSN搜索的結果。它還包含一個"X"用於關閉該小窗口。其結果欄包含塊風格的鏈接,當點擊這些鏈接時將打一個新的窗口。
圖1.結果框分為兩部分:一個頭部和一個結果欄
四、 繪制結果用戶接口 生成這個Html的代碼相當長,因為其中的元素都是使用DOM方法生成的。drawResultBox()方法接受一個參數(一個事件對象):
msnWebSearch.drawResultBox = function (e) {
var divSearchBox= document.createElement("div");
var divHeading = document.createElement("div");
var divResultsPane = document.createElement("div");
var aCloseLink = document.createElement("a");
前面這些代碼經由createElement()方法創建Html元素。在創建這些元素後,你就能夠開始賦予它們屬性。上面完成終結(封尾)的兩個元素分別是aCloseLink和divHeading:
aCloseLink.href = "#";
aCloseLink.className = "AJaxWebSearchCloseLink";
aCloseLink.onclick = this.close;
aCloseLink.appendChild(document.createTextNode("X"));
divHeading.className = "AJaxWebSearchHeading";
divHeading.appendChild(document.createTextNode("MSN Search Results"));
divHeading.appendChild(aCloseLink);
前四行完成關閉結果框的鏈接。其中,方法close()成為鏈接的onclick事件的處理器。後面的幾行代碼負責使用文本和關閉鏈接填充頭部的<div/>。
當這個結果框被繪制到頁面上時,還沒有接收到來自於一個服務器應用程序的響應。為了向用戶展示已經發生了什麼,可以向用戶展示一個消息提示數據正在加載中(這種方式更友好些)(見圖2)。為此,創建另一個元素並且把它添加到divResultsPane元素:
var divLoading = document.createElement("div");
divLoading.appendChild(document.createTextNode("Loading Search Feed"));
divResultsPane.className = "AJaxWebSearchResults";
divResultsPane.appendChild(divLoading);
這個代碼創建加載消息並且把它添加到divResultsPane,同時還把類名賦給divResultsPane。
圖2.向用戶提示數據正在加載中
完成這些元素之後,剩下的就是把它們添加到divSearchBox元素中:
divSearchBox.className = "AJaxWebSearchBox";
divSearchBox.appendChild(divHeading);
divSearchBox.appendChild(divResultsPane);
document.body.appendChild(divSearchBox);
這段代碼負責把divHeading和divResultsPane元素添加到搜索窗,並且把搜索窗添加到頁面。
在drawResultBox()中的最後一步是確定新繪制的小框的位置並且把divSearchBox返回到它的調用者:
msnWebSearch.drawResultBox = function (e) {
var divSearchBox= document.createElement("div");
var divHeading = document.createElement("div");
var divResultsPane = document.createElement("div");
var aCloseLink = document.createElement("a");
aCloseLink.href = "#";
aCloseLink.className = "AJaxWebSearchCloseLink";
aCloseLink.onclick = this.close;
aCloseLink.appendChild(document.createTextNode("X"));
divHeading.className = "AJaxWebSearchHeading";
divHeading.appendChild(document.createTextNode("MSN Search Results"));
divHeading.appendChild(aCloseLink);
var divLoading = document.createElement("div");
divLoading.appendChild(document.createTextNode("Loading Search Feed"));
divResultsPane.className = "AJaxWebSearchResults";
divResultsPane.appendChild(divLoading);
divSearchBox.className = "AJaxWebSearchBox";
divSearchBox.appendChild(divHeading);
divSearchBox.appendChild(divResultsPane);
document.body.appendChild(divSearchBox);
this.position(e, divSearchBox);
return divSearchBox;
};
通過這種方式建立msnWebSearch對象後,必須把divSearchBox返回到它的調用者以便進行其它操作。你可以已經猜出,position()方法負責放置該搜索框。它接受兩個參數:傳遞到drawResultBox()的事件對象和divSearchBox元素:
msnWebSearch.position = function (e, divSearchBox) {
var x = e.clIEntX + document.documentElement.scrollLeft;
var y = e.clIEntY + document.documentElement.scrollTop;
divSearchBox.style.left = x + "px";
divSearchBox.style.top = y + "px";
};
前兩行代碼得到左邊和頂部位置,用於放置搜索結果框。執行這個操作要求使用兩種信息。首先是鼠標的x和y坐標(這些信息被存儲在clientX和clIEntY屬性)。
然而,這些坐標還不足以正確定位結果框,因為clientX和clIEntY屬性返回相對於浏覽器窗口客戶區的鼠標位置,而不是頁面中的實際坐標。考慮到這一點,我們可以使用文檔元素的scrollLeft和scrollTop屬性。計算出最後的坐標後,你能夠最後確定用戶點擊鼠標的框中的位置。
五、 顯示結果 populateResults()方法負責使用搜索結果填充結果欄。它接受兩個參數:包含該結果的元素和一個XParser對象(XParser是一個基於Javascript的RSS讀取器,可從www.wdonline.com/Javascript/xparser/自由下載):
msnWebSearch.populateResults = function(divResultsPane,oParser){
var oFragment = document.createDocumentFragment();
divResultsPane.removeChild(divResultsPane.firstChild);
這個方法以編程方式並通過DOM方法生成<a/>元素;這樣以來,這些元素將被添加到一個在第一行創建的文檔片斷中。下一行刪除添加在drawResultBox()中的正加載的<div/>元素。
下一步是創建這個鏈接:
for (var i = 0; i < oParser.items.length; i++) {
var oItem = oParser.items[i];
var aResultLink = document.createElement("a");
aResultLink.href = oItem.link.value;
aResultLink.className = "AJaxWebSearchLink";
aResultLink.target = "_new";
aResultLink.appendChild(document.createTextNode(oItem.title.value));
oFragment.appendChild(aResultLink);
}
這段代碼遍歷回饋的各個項,並且由該數據生成鏈接並把<a/>元素添加到文檔片斷最後。
當退出循環時,該文檔片斷被添加到divResultsPane以顯示搜索結果:
divResultsPane.appendChild(oFragment);
六、 關閉結果框 為了關閉搜索結果框,msnWebSearch對象提供了close()方法。close()方法負責處理該鏈接的onclick事件(關閉該小框):
msnWebSearch.close = function () {
var divSearchBox = this.parentNode.parentNode;
document.body.removeChild(divSearchBox);
return false;
};
該搜索框其實並沒有關閉;事實上,它被從該文檔中刪除了。為此,需要檢索divSearchBox元素。第一行代碼完成這一任務-通過檢索這個元素的父結點的父結點實現。因為close()負責處理onclick事件,所以this引用這一鏈接。下一行從文檔中刪除divSearchBox元素。最後一行,返回false,從而強迫浏覽器不要沿用一個鏈接的缺省行為(轉到在href屬性中標注的位置)。
七、 構建搜索接口 msnWebSearch對象的最後一個方法是search(),它提供執行一個搜索的接口。你可以使用一個元素的onclick事件來調用search()。它接受兩個方法:一個事件對象和搜索術語:
msnWebSearch.search = function (e,sSearchTerm) {
var divSearchBox = this.drawResultBox(e);
var url = encodeURI("websearch.PHP?search=" + sSearchTerm);
var oParser = new XParser(url);
oParser.onload = function () {
msnWebSearch.populateResults(divSearchBox.childNodes[1],oParser);
};
};
第一行調用drawResultBox()方法並且把事件e傳遞給它。下一行編碼該URL以實現合適的轉換。這個URL被傳遞給XParser構造器以創建一個新的分析器。當搜索回饋完成加載並使用結果填充搜索框時,該分析器的onload事件處理器調用populateResult()方法。
當然,構建這個搜索框的一個理由是,使其更適合於你自己的站點的外觀。
八、 定制Web搜索框
借助於CSS,你可以容易地為你的現有站點定制搜索框,並且使你以後的任何重新設計都變得非常容易。
首先要討論的CSS類是AJaxWebSearchBox(該類實現搜索框)。因為搜索框要確定位置,所以它必須要有一個絕對位置:
.AJaxWebSearchBox
{
position: absolute;
background-color: #0d1e4a;
width: 500px;
padding: 1px;
}
在此,絕對位置是唯一的要求。所有的其它屬性都是根據你的口味可選的。在這個示例中,該框有一個微藍色的背景,一個500像素的寬度,以及在四邊上各有1個像素的填充。這個填充導致圍繞該框的內容的是1個像素寬的邊界。
下一個類是ajaxWebSearchHeading,它包含該框的頭部文本和關閉鏈接。為了把關閉鏈接放在右上角,它使用絕對位置。因為這個原因,它要求AJaxWebSearchHeading使用一個相對位置:
.AJaxWebSearchHeading
{
position: relative;
background-color: #1162cc;
font: bold 14px tahoma;
height: 21px;
color: white;
padding: 3px 0px 0px 2px;
}
在此,唯一要求的屬性也是position屬性。其它的屬性有助於給出該元素一個好看的外觀。其背景顏色是淺藍色,而文本部分是白色,14像素高且是Tahoma字體。該元素的高度是21個像素並且在頂部和左邊都填充以邊界。
如前面所述,該關閉鏈接的位置是絕對的:
a.AJaxWebSearchCloseLink
{
position: absolute;
right: 5px;
top: 3px;
text-decoration: none;
color: white;
}
a:hover.AJaxWebSearchCloseLink
{
color: red;
}
該元素被放置在距右邊5個像素,距頂部3個像素的位置(該元素被放在右上角)。這個鏈接沒有任何文本修飾並且顏色呈白色。當用戶的鼠標停在該鏈接上時,文本顏色變紅。
注意,這裡沒有使用訪問過的或活動的"假"類。這是因為該窗口總是忽略這個鏈接的href屬性(它已經在它的事件處理器中返回了false)。因此,該鏈接從來不會真正處於活動或被訪問狀態。
然後,AJaxWebSearchResults類使結果欄的風格如下:
.AJaxWebSearchResults
{
background-color: #d3e5fa;
padding: 5px;
}
這個元素不要求使用CSS屬性。現有屬性僅用於定義結果欄並且使它比較容易讀取。背景顏色是一個淺藍色並且圍繞邊緣有5個像素的填充。當然,你能夠定制加載消息的風格:
.AJaxWebSearchResults div
{
text-align: center;
font: bold 14px tahoma;
color:#0a246a;
}
這個元素沒有一個類名,但是你仍然能夠通過使用前面的示例中展示的parent child標志控制它的風格。這個示例把文本放置在<div/>元素的中央,並且給它一個加粗藍色的字體,且有14個像素高。
最後一個你需要風格化的元素是結果鏈接。這些鏈接有一個類名叫AJaxWebSearchLink:
a.AJaxWebSearchLink
{
font: 12px tahoma;
padding: 2px;
display: block;
color: #0a246a;
}
a:hover.AJaxWebSearchLink
{
color: white;
background-color: #316ac5;
}
a:visited.AJaxWebSearchLink
{
color: purple;
}
唯一要求的屬性是display屬性(被設置為block)。這使每一個鏈接都能夠在它自己的行上顯示。填充空白部分大約有兩個像素寬,使各個鏈接之間分開一些,從而使它們更易於讀取。字體名為Tahoma並且有12像素高。它們的顏色是暗藍色,與AJaxWebSearchResults的淺藍色背景形成對照。
當用戶在這些鏈接上移動鼠標時,背景顏色被設置為藍色,而文本顏色改變為白色。
在前面的代碼的最後一條規則中訪問過的"假"類被設置。這是為了給用戶提供用戶接口暗示-它們已經被使用過。通過把訪問過的"假"類設置為顯示一種紫色,用戶就可以知道它們已經訪問過那個鏈接,從而節省他們的時間-不必再訪問一個他們可能不想看的頁面。
現在,讓我們來看一下如何實現搜索框。
九、 實現Web搜索搜索框
實現這個搜索框是很簡單的。首先,你必須把websearch.php文件上傳到你的web服務器(當然,必須安裝PHP)。然後,你需要一個Html文檔來引用所有的組件。msnWebSearch對象依賴於XParser類,這個類又依賴於zXML庫(可從www.nczonline.Net/downloads/下載)。你必須引用下面這些文件:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHtml 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xHtml1-transitional.dtd">
<html xml:lang="en" lang="en" XMLns="http://www.w3.org/1999/xHtml">
<head>
<meta http-equiv="Content-Type" content="text/Html; charset=utf-8" />
<title>AJax WebSearch</title>
<link rel="stylesheet" type="text/css" href="css/websearch.CSS" />
<script type="text/Javascript" src="JS/zXML.JS"></script>
<script type="text/Javascript" src="js/xparser.JS"></script>
<script type="text/Javascript" src="js/websearch.JS"></script>
</head><body>
</body>
</Html>
為了執行搜索,應該把msnWebSearch.search()方法設置為該元素的onclick處理器:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHtml 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xHtml1-transitional.dtd">
<html xml:lang="en" lang="en" XMLns="http://www.w3.org/1999/xHtml">
<head>
<meta http-equiv="Content-Type" content="text/Html; charset=utf-8" />
<title>AJax WebSearch</title>
<link rel="stylesheet" type="text/css" href="css/websearch.CSS" />
<script type="text/Javascript" src="JS/zXML.JS"></script>
<script type="text/Javascript" src="js/xparser.JS"></script>
<script type="text/Javascript" src="js/websearch.JS"></script>
</head><body>
<a href="#" onclick='msnWebSearch.search(event,"\"Professional AJax\"");
return false;'>Search for "Professional AJax"</a>
<br /><br /><br /><br />
<a href="#" onclick='msnWebSearch.search(event,"Professional AJax");
return false;'>Search for Professional AJax</a>
</body>
</Html>
第一個新的鏈接執行一個針對准確詞組"Professional AJax"的搜索,而第二個鏈接將搜索這其中的各個單詞。還要注意,在onclick事件中返回的是false-這強迫浏覽器忽略掉href屬性。點擊這些鏈接將在光標位置繪制搜索框,並且就在此處顯示你的搜索結果。