一、 引言
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建立-它被定義為一個沒有任何屬性(現在)的對象:
這個對象用於一個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"用於關閉該小視窗。其結果欄包含塊風格的連結,當點擊這些連結時將打一個新的視窗。
四、 繪製結果使用者介面
產生這個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。
完成這些元素之後,剩下的就是把它們添加到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屬性。點擊這 些連結將在游標位置繪製搜尋方塊,並且就在此處顯示你的搜尋結果。