即時功能入門
常用縮減語
- Ajax:非同步 JavaScript + XML
- CSS:層疊樣式表
- DOM:文件物件模型
- HTML:超文字標記語言 (HTML)
- JSON:JavaScript 對象符號
- UI:使用者介面
- URL:統一資源定位器
- XML:可延伸標記語言 (XML)
Google 的即時功能是一種新的搜尋增強功能,隨著您的鍵入顯示結果,它已經獲得了眾多矚目,而且很容易明白這是為什麼。擷取結果所需做的全部工作就是鍵入。您無需按 Enter 鍵來查看結果,然後調整您的搜尋並再次按 Enter 鍵。這都將隨著您的鍵入而發生。如果您還沒有這樣做,請嘗試一下。令人驚訝的是這麼小的變化卻能在可用性上產生如此大的差異。
這類即時功能的主要好處在於易於實現,尤其當您在使用 jQuery 等用戶端工具時(參見 參考資料)。在本文中,您要遵循構建簡單搜尋引擎的流程,而後再為該引擎構建立即搜尋使用者介面。
這一切都始於擷取搜尋資料。
設定資料
對於本文,我決定搜尋 “辛普森一家” 集。我建立一個包含所有 “辛普森一家” 集、標題、季數、集數、播放日期和每集摘要的 XML 檔案(包括在原始碼 下載 中)。您可以在 清單 1 中看到該 XML 的一部分。
清單 1. XML 資料來源
<?xml version="1.0" encoding="UTF-8"?> <episodes> <episode title='Simpsons Roasting on an Open Fire' episode='1' season='1' aired='17 December 1989'> Christmas seems doomed for the Simpson family when Homer receives no Christmas Bonus. Homer becomes a mall Santa Claus, hoping to make money and bring Marge, Bart, Lisa, and baby Maggie, a happy holiday. </episode> ... </episodes> |
它實際上是一個非常大的檔案,其大小約為 840K。這應該不令人意外,因為 “辛普森一家” 已經播放了漫長的 22 年。
接下來就是編寫一個 PHP 類,可為您執行 XML 解析和搜尋。這個類稱為 Simpsons,如 清單 2 中所示。
清單 2. Simpsons 搜尋類
<?php class Simpsons { private $episodes = array(); public function __construct() { $xmlDoc = new DOMDocument(); $xmlDoc->load("simpsons.xml"); foreach ($xmlDoc->documentElement->childNodes as $episode) { if ( $episode->nodeType == 1 ) { $this->episodes []= array( 'episode' => $episode->getAttribute( 'episode' ), 'season' => $episode->getAttribute( 'season' ), 'title' => $episode->getAttribute( 'title' ), 'aired' => $episode->getAttribute( 'aired' ), 'summary' => $episode->nodeValue ); } } } public function find( $q ) { $found = array(); $re = "/".$q."/i"; foreach( $this->episodes as $episode ) { if ( preg_match( $re, $episode['summary'] ) || preg_match( $re, $episode['title'] ) ) { $found []= $episode; } } return $found; } } ?> |
該類的建構函式使用對於 PHP 來說標準的 XML DOM 庫讀取集資訊的 XML 檔案。它迭代根節點的所有子節點並提取它們的季數、標題、播放日期和集屬性,以及包含摘要的節點的文本。然後將所有資料作為一個雜湊表附加到集數組,該數組是一個成員變數。
然後,find 函數搜尋集列表以便使用與標題和摘要匹配的簡單Regex來尋找匹配項。任何匹配的集都被附加到一個數組,然後返回給調用方。如果數組為空白,則沒有發現匹配項。
現在有了資料,下一步就是開始構建 Ajax 響應程式,您的即時 UI 將調用該響應程式來檢索資料。
建立 Ajax 響應頁面
第一個版本的 UI 針對 Ajax 請求使用 HTML 響應。此方法是實現即時 UI 的最簡單的方式。即時 UI web 頁面採用搜尋關鍵詞並使用該關鍵詞對伺服器發出 Ajax 請求。然後,伺服器格式化組成該響應的 HTML 塊並將其返回到頁面。在一個簡單的調用中,即時 UI web 頁面中的代碼將使用更新的 HTML 替換該頁面的一部分。
在本文的後面,我會示範使用來自伺服器的 XML 響應和 JSON 響應,但是現在,為了簡單起見,我們從 HTML 版開始。
您首先需要的是 HTML 響應頁面。此頁面接受來自請求的查詢字串。然後使用該字串調用 Simpsons 類來搜尋集。接著,將已返回的集數組格式化 HTML。此代碼位於 清單 3 中。
清單 3. HTML Ajax 響應頁面
<?php include 'Simpsons.php';$s = new Simpsons(); $episodes = $s->find( $_REQUEST['q'] ); if ( count( $episodes ) == 0 ) { ?> No results found <?php } else { ?> <table> <?php foreach( $episodes as $e ) { ?> <tr><td class="episode"><b><?php echo( $e['title'] ) ?></b> - Season <?php echo( $e['season'] ) ?> Episode <?php echo( $e['episode'] ) ?> - Aired on <?php echo( $e['aired'] ) ?></td></tr> <tr><td class="summary"><?php echo( $e['summary'] ) ?></td></tr> <?php } ?> </table> <?php } ?> |
在頂部,清單 3 包括 Simpsons 類。然後該代碼建立該類的一個新執行個體並進行 find 調用。之後它會查看響應是否為空白,以及是否返回 “No Results Found”;否則,它遍曆這些結果並組成一個結果表。
為了測試該頁面,只需轉到您的 網頁瀏覽器並請求該網頁。您可以在 圖 1 中看到輸出。
圖 1. HTML Ajax 響應頁面
此時,您擁有開始構建立即搜尋 UI 所需要的所有東西。
回頁首
構建立即搜尋 UI
使用 JavaScript jQuery 庫可以輕鬆構建立即搜尋 UI。查看 清單 4,您就可以明白我的意思。
清單 4. 使用 HTML 響應的即時頁面
<html><head> <script src="jquery-1.4.2.min.js"></script> <link rel="stylesheet" href="styles.css" type="text/css" /> <title>Instant Search - HTML Based</title> </head> <body> Simpsons Search: <input type="text" id="term" /> <div id="results"> </div> <script> $(document).ready(function() { $('#term').keyup(function() { $.get('search_html.php?q='+escape($('#term').val()), function(data) { $('#results').html(data); } ); } ); } ); </script> </body> </html> |
在頁面的頂部,清單 4 包括 jQuery 庫和 CSS 樣式表,以使輸出更美觀。頁面的主體包括搜尋關鍵詞的輸入欄位和儲存此輸出的結果 div。
工作的大部分都在頁面底部的 JavaScript 部分完成。它首先調用文檔中的 ready 函數。此調用可確保在頁面準備就緒前都不執行內部 JavaScript。內部 JavaScript 使用搜尋關鍵詞輸出對象中的 keyup 函數來監控搜尋關鍵詞欄位中的關鍵字鍵入。在文字欄位變更時,將對伺服器調用 Ajax get 方法。通過使用 html 函數,來自此調用的資料響應將用於填充結果元素。
如果 JavaScript 代碼看上去像線路雜訊,沒關係。這實際上就是 JavaScript 的技術現狀,該代碼需要複查線路,所以代碼規模最好保持得小一些。
雖然您可以在沒有 jQuery 庫的情況下完成這些工作,但是使用該庫的價值在於,該代碼非常簡潔且所有跨平台工作都已經為您完成。您不必擔心 Internet Explorer 與 Safari 或 Firefox;只需編寫一次代碼然後將其用於任何地方。
要測試該庫,請在 網頁瀏覽器中建立立即搜尋 UI。在 圖 2 中,您可以看到類似的東西。
圖 2. 在搜尋關鍵詞中鍵入的少量字母
圖 2 顯示了我輸入少量字元後的介面。在我鍵入關鍵詞 “frink” 之後,您可以在 圖 3 中看到結果。
圖 3. 鍵入關鍵詞之後
圖 3 顯示了在標題或兩集概要中出現 “frink”。可笑的資料!Frink 教授(到目前為止節目中的最佳角色)出現在不止兩集中。但這仍是非常奇妙。本機電腦上的回應時間是非常出色的,即使伺服器代碼解析是通過 840K 的 XML。
現在您可能想通過在每一個按鍵之間放置一個延遲並在您真正發出請求時來控制請求的數量。在 清單 5 中,更新的代碼可以執行此操作。
清單 5. 使用帶有延遲的 HTML 響應的即時頁面
<html><head> <link rel="stylesheet" href="styles.css" type="text/css"> <script src="jquery-1.4.2.min.js"></script> <title>Instant Search - HTML Based With Delay</title> </head> <body> Simpsons Search: <input type="text" id="term" /> <div id="results"> </div> <script> delayTimer = null;function getResults() { $.get('search_html.php?q='+escape($('#term').val()), function(data) { $('#results').html(data); } ); delayTimer = null; } $(document).ready(function() { $('#term').keyup(function() { if ( delayTimer ) window.clearTimeout( delayTimer ); delayTimer = window.setTimeout( getResults, 200 ); } ); } ); </script> </body> </html> |
此代碼在使用者按鍵時建立一個計時器。當該計時器在 200 毫秒後停止時,請求發出。如果另外一次擊鍵在計時器停止之前,則原來的計時器將被取消,同時建立一個新的計時器。其結果就是在使用者停止了鍵入之後,計時器停止 200 毫秒。雖然該介面始終感覺像原來的,但是向伺服器發出請求的數量會大幅減少,尤其是在使用者快速鍵入的時候。
雖然我們可以到此為止,但是實際上有另外兩種方式來進行此即時過程。
遷移到 XML
第一種方式是使用 XML 作為您從伺服器到用戶端的傳輸文法。此處的想法是伺服器提供一個通用 XML 端點,任何流程都可以使用該端點來執行查詢,並且您的用戶端足夠智能,能夠讀取 XML 並以其想要的方式來格式化它。
要變成 XML,首先要建立如 清單 6 中所示的新伺服器頁面。
清單 6. XML Ajax 頁面
<?php include 'Simpsons.php';header( 'Content-type: text/xml' ); $s = new Simpsons(); $doc = new DOMDocument(); $root = $doc->createElement( 'episodes' ); $doc->appendChild( $root ); foreach( $s->find( $_REQUEST['q'] ) as $episode ) { $el = $doc->createElement( 'episode' ); $el->setAttribute( 'title', $episode['title'] ); $el->setAttribute( 'episode', $episode['episode'] ); $el->setAttribute( 'season', $episode['season'] ); $el->setAttribute( 'aired', $episode['aired'] ); $tn = $doc->createTextNode( $episode['summary'] ); $el->appendChild( $tn ); $root->appendChild( $el ); } print $doc->saveXML(); ?> |
雖然搜尋仍然一樣,但是您格式化結果的方式變了。現在,該代碼建立了 XML 文檔並將節點附加到其上,從而儲存所有返回的資料。然後在指令碼的末尾,它只是將 XML DOM 儲存為字串。請注意在指令碼的頂部,在匯出 XML 而非 HTML 時,還要將內容類型設定為文本/xml。
如果在您的 網頁瀏覽器中導航到此頁面,則您會看到類似 圖 4 中的情況。
圖 4. XML 響應頁面
不過,一些瀏覽器可能以更結構化的方式顯示已返回的文本。如果您想查看原始的源 XML,則您可以選擇 View - Source 以便看到類似 圖 5 中的視窗。
圖 5. XML 響應頁面原始碼
如您所見,該指令碼建立了一些良好格式化的 XML,準備好供新的用戶端代碼使用。
解析 XML(而不是直接使用 HTML)的新用戶端代碼位於 清單 7 中。
清單 7. 使用 XML 的立即搜尋頁面
<html><head> <script src="jquery-1.4.2.min.js"></script> <link rel="stylesheet" href="styles.css" type="text/css" /> <title>Instant Search - XML Based</title> </head> <body> Simpsons Search: <input type="text" id="term" /> <table id="results"> </table> <script> $(document).ready( function() { $('#term').keyup( function() { $.get('search_xml.php?q='+escape($('#term').val()), function(data) { html = '<table id="results">'; $(data).find('episode').each( function() { var ep = $(this); html += '<tr><td class="episode"><b>'+ ep.attr('title')+'</b> '; html += 'Season '+ep.attr('season')+' '; html += 'Episode '+ep.attr('episode')+' '; html += 'Aired '+ep.attr('aired')+'</td></tr>'; html += '<tr><td class="summary">'+ ep.text()+'</td></tr>'; } ); html += '</html>'; $('#results').replaceWith( html ); } ); } ); } ); </script> </body> </html> |
用於監控擊鍵和發出 Ajax 請求的用戶端代碼幾乎完全一樣。所不同的是不同的 URL 擷取 XML 資料而不是 HTML 資料。
在資料返回以後,該代碼使用 jQuery 來尋找所有集標籤。然後它可格式化大量 XML 並使用 replaceWith 函數來用新表替換舊錶。由於使用了 jQuery,此代碼比在使用瀏覽器的原生 DOM 功能時更容易使用。
傳輸資料的另一種方式是通過 JSON(JavaScript 對象符號)。
遷移到 JSON
在 Web 2.0 的世界中,JSON 是一種非常流行的移動資料的方式。它小巧、方便、快捷,便於瀏覽器讀取,因為需要做的所有操作就是評估已返回的 JavaScript 代碼。建立 JSON 也很簡單,就如您在 清單 8 中的 Ajax 搜尋網頁面的 JSON 版本中所見的那樣。
清單 8. JSON Ajax 頁面
<?php include 'Simpsons.php';header( 'Content-type: application/json' ); $s = new Simpsons(); print json_encode( $s->find( $_REQUEST['q'] ) ); ?> |
您只需使用 json_encode 函數來將已返回的數組轉變為 JSON 代碼。如果您好奇的話,這裡也存在一個可將 JSON 轉回為 PHP 基本類型的 json_decode 函數。大多數流行語言都具有與這裡一樣簡單的 JSON 機制,可以將基本資料結構轉化為 JSON,或者轉換出 JSON。
如果您在瀏覽器中查看 JSON 頁面,則您會看到類似 圖 6 中的響應頁面。
圖 6. JSON 響應頁面
雖然本頁面可能不會太吸引人們的目光,但是對於瀏覽器中的 JavaScript 解譯器來說,此頁面看起來非常易於閱讀。
用於讀取 JSON 格式化輸出的相應的即時 UI web 代碼位於 清單 9 中。
清單 9. JSON 立即搜尋 UI
<html><head> <script src="jquery-1.4.2.min.js"></script> <link rel="stylesheet" href="styles.css" type="text/css" /> <title>Instant Search - JSON Based</title> </head> <body> Simpsons Search: <input type="text" id="term" /> <table id="results"> </table> <script> $(document).ready( function() { $('#term').keyup( function() { $.get('search_json.php?q='+escape($('#term').val()), function(data) { html = '<table id="results">'; $.each( data, function( ind, ep ) { html += '<tr><td class="episode"><b>'+ep.title+'</b> ';s html += 'Season '+ep.season+' '; html += 'Episode '+ep.episode+' '; html += 'Aired '+ep.aired+'</td></tr>'; html += '<tr><td class="summary">'+ep.summary+'</td></tr>'; } ); html += '</html>'; $('#results').replaceWith( html ); } ); } ); } ); </script> </body> </html> |
此代碼非常類似於 XML 代碼,不同的是您可以對返回的數組使用 jQuery each 函數,然後使用點符號來訪問資料中的所有重要關鍵字(也就是說,標題、集、摘要等等)。
現在您具有:可以用作您自己的工作起始點的立即搜尋功能的初步實現。
一點補充
與 Google 的開發人員所做的相比,此實現具有三個主要區別。首先是縮放。他們每天要處理數十億的搜尋,現在通過每一次擊鍵他們可處理數十億單獨的小搜尋。雖然存在許多有關這方面的問題和解決方案,但是在這種情況下您至少有一件事情要去做 — 瀏覽器緩衝。如果使用者鍵入相同的關鍵詞兩次,由於瀏覽器緩衝的緣故,實際上僅進行一個請求,因為第二次發出請求時瀏覽器返回緩衝的資料。
Google 所作的另外一件事情是預取結果。例如,如果您鍵入 “mov”,則暗示您正在尋找 “movies”,進行此搜尋並通過用於您缺少的 “ies” 的灰色文本指示這點。
第三個區別是對於分頁的支援,這相當容易解決。您所要做的就是在頁面的底部為頁面連結添加一些 JavaScript,然後在使用者單擊以從第一頁瀏覽到任何隨後頁面時調用此指令碼。
結束語
Google 的即時 UI 功能真的很即時。它是革命性的嗎?不是。但是它是對可用性具有深遠影響的一小步。正如您從本文中看到的,通過使用像 XML、PHP 和 jQuery 這樣的標準工具,這些行為的初步階段並不難實現。
我希望您能夠在您的項目中使用本文所提供的代碼。如果您這樣做了請告之我。我會很高興看到您是如何使用它的。
本文轉自www.35java.com