自動完成器的承載容器是一個 input 元素 :
<input id="auto1" type="text" />
但接下來與其他 widget 不同的是, 自動完成器在呼叫 autocomplete() 函式時, 必須傳入 source 選項提供資料來源才行, 否則將因無資料可比對而無作用. 選項 source 有三種形式: 陣列, 字串, 或回呼函式. 首先介紹陣列形式, 這是資料來源為本地 (即程式本身所提供) 時用的形式. 這個陣列有兩種, 第一種文字陣列, 如範例 1 所示 :
測試範例 1 : http://tony1966.xyz/test/jquerytest/autocomplete_1.htm [看原始碼]
範例 1 中我們放了三個 input 元素, 第一個用來輸入股票名稱, 後面兩個用來輸入股票號碼 (但第三個選項是錯誤的示範) :
var stock_name=["味王", "車王電", "葡萄王", "王品", "花王", "緯創", "創意"]; //正確的
var stock_id=["1203", "1533", "1707", "2727", "8906", "3231", "3443"]; //正確的
var stock_id_number=[1203, 1533, 1707, 2727, 8906, 3231, 3443]; //錯誤的
$("#auto1").autocomplete({source: stock_name});
$("#auto2").autocomplete({source: stock_id});
$("#auto3").autocomplete({source: stock_id_number});
當我們在 auto1 輸入 "王" 時, 自動完成器就會把含有 "王" 的股票名稱找出來作為下拉式選單的選項 (共五項), 輸入 "創" 則會找到兩個選項. 特別注意, 陣列的元素必須是文字, 不可以用數值, 否則選項將不會顯示, 這就是 auto3 測試所呈現的結果.
接著在範例 2 要介紹第二種陣列-物件陣列 (其實就是 JSON 格式之陣列), 陣列的每一個元素必須要有 label 與 value 兩個 key (即屬性), 其中 label 用來顯示於下拉式選單中的選項, 必須是字串; 而 value 則是使用者選取後填入 input 欄位中的值, 可以用數值, 但建議一律使用字串. 其次, 要介紹 delay, minlength 這兩個選項的用法. 選項 delay 是向 source 選項指定之資料來源發出請求或擷取動作之前的等待毫秒數. 預設為 300 毫秒 (0.3 秒). 因為透過網路取得取得非本地端資料來源時速度較慢, 利用 delay 延遲可以讓使用者多輸入幾個字元再送出請求可以縮小資料量. 對於本地資料 (陣列), 可以將此選項設為 0 以加快反應時間. 而 minLength 是設定要輸入多少字元才會觸發向 source 選項指定之資料來源擷取資料的動作. 預設值為 1. 當符合的資料量很多時, 可以利用此選項來限縮資料量至合理範圍. 設為 0 時, 只要隨意輸入一個字元再刪除, 就會觸發資料擷取動作, 顯示全部選項.
範例 2 中我們放置了兩個 input 元素 : auto1 與 auto2, 兩個自動完成器的物件陣列之 label 與 value 剛好顛倒, auto1 的 delay 與 minlength 都設為 0, 所以反應時間很快; 而 auto2 的 delay 設為 1000 (1 秒), 因此雖然是本地資料, 感覺反應較慢, 其 minLength 設為 2, 須輸入兩個字元才會觸發自動完成動作. 請注意, 如果是本地端提供的資料, 兩個屬性名稱有沒有加引號都可以, 亦即 label 與 value 或 "label" 與 "value" 均可. 但是, 如果資料是由遠端伺服器提供, 則屬性名稱務必加上引號. 請參考下面的範例 5.
測試範例 2 : http://tony1966.xyz/test/jquerytest/autocomplete_2.htm [看原始碼]
$(document).ready(function(){
var stock_name=[{"label":"味王", "value":1203},
{label:"車王電", value:"1533"},
{label:"葡萄王", value:"1707"},
{label:"王品", value:"2727"},
{label:"花王", value:"8906"},
{label:"緯創", value:"3231"},
{label:"創意", value:"3443"}];
var stock_id=[{label:"1101", value:"台泥"},
{label:"1102", value:"亞泥"},
{label:"1103", value:"嘉泥"},
{label:"2204", value:"中華"},
{label:"2207", value:"和泰車"},
{label:"2227", value:"裕日車"},
{label:"2206", value:"裕隆"}];
$("#auto1").autocomplete({source: stock_name, delay: 0, minLength: 0});
$("#auto2").autocomplete({source: stock_id, delay: 1000, minLength: 2});
從上面兩個範例可知, 字串陣列事實上是物件陣列的一種特例, 也就是說字串陣列是 label=value 的物件陣列, 亦即下列兩個是等效的 :
var stock_name=["味王", "車王電"];
var stock_name=[{label:"味王", value:"味王"}, {label:"車王電", value:"車王電"}];
其實物件陣列不一定同時需要 lable 與 value 這兩個屬性, 單獨一個屬性也是可以的, 只有 label 時, 表示 value 等於 label, 反之亦然, 總之, 其中一個屬性沒提供的話, 就會用另一個代替, 下列範例 3 我們就來測試看看 :
測試範例 3 : http://tony1966.xyz/test/jquerytest/autocomplete_3.htm [看原始碼]
在範例 3 中, 我們放了兩個 input 元素, 第一個 auto1 的物件陣列中, 只有一個屬性 label, 選取後之值就是 label; 而第二個 auto2 則只有一個屬性 value, 顯示的 label 就是 value.
var stock_name=[{label:"味王"},
{label:"車王電"},
{label:"葡萄王"},
{label:"王品"},
{label:"花王"},
{label:"緯創"},
{label:"創意"}];
var stock_id=[{value:"1101"},
{value:"1102"},
{value:"1103"},
{value:"2204"},
{value:"2207"},
{value:"2227"},
{value:"2206"}];
$("#auto1").autocomplete({source: stock_name});
$("#auto2").autocomplete({source: stock_id});
所以跟範例 1 效果一樣, 也就是說, 如果顯示用的 label 與設值的 value 一樣時, 其實用單純的文字陣列就可以了, 沒必要用物件陣列, 只有 "表裡不一" 時才需要用物件陣列, 例如下拉式選單顯示 "2330 (台積電)", 但選取後設定在輸入框的是 2330 (後端資料庫搜尋時的 key).
接著來介紹 source 選項為字串的用法. 這個字串是一個指向遠端伺服器的 URL 字串, 也就是說, 自動完成器內部對於 source 為 URL 的情況內建了一個 Ajax 機制, 向遠端伺服器要求提供資料作為下拉式選單選項的資料來源. 自動完成器會將使用者輸入的字串以名稱為 term 的網頁參數傳給伺服器, 後端的 PHP 程式可以用 $_GET["term"] 或 $_REQUEST["term"] 取得使用者輸入的字眼. 程式傳回的資料格式必須是 JSON, 以 PHP 而言, 可以將回應資料先存入字串陣列中, 最後再呼叫 json_encode() 函式即可轉成 JSON 格式.
在下面的範例 4 我們要測試從遠端資料庫中擷取股票名稱或代號, 顯示於自動完成器的下拉式選單中. 所以要先在 MySQL 中建立一個資料表 stock_list, 它只有兩個文字欄位 : stock_id 與 stock_name, 分別用來儲存股票代號與名稱. 建立此資料表的 SQL 指令如下 :
CREATE TABLE IF NOT EXISTS `stocks_list` (
`stock_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`stock_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL
PRIMARY KEY (`stock_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
然後把資料 INSERT 進去, 由於資料很多, 指令僅擷取片段說明 :
INSERT INTO `stocks_list` (`stock_id`, `stock_name`) VALUES
('0015', '富邦'),
('0050', '台灣50'),
('0051', '中100'),
.....
('9955', '佳龍'),
('9958', '世紀鋼');
完整的 SQL 指令檔可從下列 URL 下載 :
stocks_list.sql http://tony1966.xyz/test/jquerytest/stocks_list.sql
只要將此 SQL 指令檔灌進 (載入/輸入) MySQL 資料庫即可.
後端資料庫安排妥當後, 接下來看前端網頁與程式. 在網頁中我們放置了兩個 input 元素 : auto1 用來輸入股名; auto2 用來輸入股號 :
股名 : <input gt="" id="auto1" p="" type="text" />
股號 : <input gt="" id="auto2" p="" type="text" />
Javascript 程式部分, 我們設定兩個自動完成器的 source 屬性為遠端的 PHP 程式, 這兩個程式必須傳回 JSON 格式的回應 :
var url_1="http://tony1966.xyz/test/jquerytest/get_stocks_name.php";
var url_2="http://tony1966.xyz/test/jquerytest/get_stocks_id.php";
$("#auto1").autocomplete({source: url_1});
$("#auto2").autocomplete({source: url_2});
現在剩下兩個後端 PHP 程式了, 先來看一下 get_stocks_name.php, 先連線資料庫, 並取得自動完成器傳出之 term 參數 (即使用者輸入的字串) 以製作 SQL 查詢指令. 注意, 這裡用 LIKE '%大%' 條件來查詢 stock_name 欄位值含有 '大' 者. 然後呼叫 PHP 的 mysql_query 取得記錄集, 再呼叫 mysql_fetch_array() 函式來取得每一列, 再把其中的 stock_name 欄位值儲存在陣列中, 最後呼叫 json_encode() 函式把股名陣列轉成 JSON 格式字串傳回.
<?php
header('Content-Type: text/html;charset=UTF-8');
$host="mysql.1freehosting.com"; //MySQL 主機位址
$username="tony1966_test"; //MySQL 使用者名稱
$password="123456"; //MySQL 使用者密碼
$database="testdb"; //資料庫名稱
$conn=mysql_connect($host, $username, $password); //建立連線
mysql_query("SET NAMES 'utf8'"); //設定查詢所用之字元集為 utf-8
mysql_select_db($database, $conn); //開啟資料庫
$term=$_GET['term']; //擷取 jQueryUI 傳出參數 'term'
$SQL="SELECT * FROM `stocks_list` WHERE `stock_name` LIKE '%".$term."%'";
$result=mysql_query($SQL, $conn); //執行 SQL 指令
$arr=Array(); //用來儲存 stock_name 欄位值之陣列
for ($i=0; $i
$arr[]=$row["stock_name"]; //放入陣列
} //end of for
echo json_encode($arr); //將陣列轉成 JSON 資料格式傳回
?>
另一個 PHP 程式 get_stocks_id.php 只要將 stock_name 改為 stock_id 即可.
測試範例 4 : http://tony1966.xyz/test/jquerytest/autocomplete_4.htm [看原始碼]
上述範例 4 是股票名稱與代號分開, 現在想要合而為一, 只需要一個文字框, 使用者可以輸入股名或股號, 都會觸發自動完成器的 Ajax 查詢動作, 而且選單會顯示例如 "2330 (台積電)", 選取後文字框會自動填入代號 2330, 這要怎麼做? 其實只要修改一下網頁與後端 PHP 查詢程式即可. 在下列範例 5 中, 網頁中只放置一個 input 元素 :
而後端 PHP 查詢程式則改為 get_stocks_list.php 如下 :
<?php
header('Content-Type: text/html;charset=UTF-8');
$host="mysql.1freehosting.com"; //MySQL 主機位址
$username="tony1966_test"; //MySQL 使用者名稱
$password="123456"; //MySQL 使用者密碼
$database="testdb"; //資料庫名稱
$conn=mysql_connect($host, $username, $password); //建立連線
mysql_query("SET NAMES 'utf8'"); //設定查詢所用之字元集為 utf-8
mysql_select_db($database, $conn); //開啟資料庫
$term=$_GET['term']; //擷取 jQueryUI 傳出參數 'term'
$SQL="SELECT * FROM `stocks_list` WHERE `stock_id` LIKE '".$term.
"%' OR `stock_name` LIKE '".$term."%'";
$result=mysql_query($SQL, $conn); //執行 SQL 指令
$arr=Array(); //用來儲存 stock_id 欄位值之陣列
for ($i=0; $i < mysql_numrows($result); $i++) { //走訪紀錄集 (列)
$label=$row["stock_id"]." (".$row["stock_name"].")";
$value=$row["stock_id"];
$stock=Array("label" => $label, "value" => $value);
$arr[]=$stock; //放入陣列
} //end of for
echo json_encode($arr); //將陣列轉成 JSON 資料格式傳回
與前面範例 4 不同的是, 此處的 $SQL 指令在搜尋 stock_id 與
http://tony1966.xyz/test/jquerytest/get_stocks_list.php?term=%E5%89%B5
[{"label":"2451 (\u5275\u898b)","value":"2451"},{"label":"3443 (\u5275\u610f)","value":"3443"}]
可見 json_encode() 會把中文轉成字元實體. 關於 json_encode() 的使用, Tsung's Blog 有一篇文章可參考 : "PHP 讓 json_encode() 指定回傳格式".
$arr=Array(); //用來儲存 stock_id 欄位值之陣列
for ($i=0;
$row=mysql_fetch_array($result); //取得列陣列
$label=$row["stock_id"]." (".$row["stock_name"].")";
$value=$row["stock_id"];
$arr[]='{"label":"'.$label.'","value":"'.$value.'"}'; //放入陣列
} //end of for
echo "[".join(",", $arr)."]"; //輸出 JSON 格式字串
同樣向 get_stocks_list_2.php 查詢股名以 "創" 字開頭者, 它的回應如下 :
http://tony1966.xyz/test/jquerytest/get_stocks_list_2.php?term=%E5%89%B5
[{"label":"2451 (創見)","value":"2451"},{"label":"3443 (創意)","value":"3443"}]
這樣也是 OK 的, 請參考範例 6.
http://tony1966.xyz/test/jquerytest/get_stocks_list_3.php?term=%E5%89%B5
[{'label':'2451 (創見)','value':'2451'},{'label':'3443 (創意)','value':'3443'}]
以這個回應做成範例 7 可知, 這樣是不行的 :
測試範例 7 : http://ㄒ/jquerytest/autocomplete_7.htm [看原始碼] (無效)
哇, 光是測試 source=url 就花了這麼多口水, 不過通過這些測試也了解了自動完成器的一些脾氣, 這些在官網卻沒提到. 總結一下 : source 是遠端伺服器時, 回應的 JSON 物件陣列中, 屬性名稱 label 與 value 務必要用雙引號括起來.
下面介紹 source 的第三種類型 : 回呼函式. 據書上說這是自動完成器最強的部份, 因為它的彈性最大, 可以透過回呼函式操控輸出結果. 這個回呼函式有兩個傳入參數 request 與 response, 使用者輸入的字眼 (term) 是透過 request 物件唯一的 term 屬性取得的 :
var term=request.term; //取得使用者輸入的查詢字串
然後函式要產生一個結果陣列 result, 此陣列可以是上述所說的文字陣列或 JSON 物件陣列. 然後呼叫 response() 把結果陣列傳回去就搞定了 :
response(result);
下面範例 8 我們就用一個超簡單功能來測試一下. 從以上測試結果可知, 若資料來源為本地陣列, 比對的方式是 "contains", 亦即陣列元素的 label 是否包含使用者輸入的字眼, 這相當於 SQL 的 LIKE '%字眼%' 條件. 如果要比對起始字串是否符合該怎麼辦呢呢? 哪是有這款的症頭, 不是打 "控八控控" 喔, 而是要把陣列交給回呼函式處理一下.
測試範例 8 : http://tony1966.xyz/test/jquerytest/autocomplete_8.htm [看原始碼]
範例 8 我們對股名與股號分別放了 input 元素 :
股名 (輸入 "中","台","創") : <input id="auto1" type="text" />
股號 (輸入 "1","2","3") : <input id="auto2" type="text" />
程式部分, 我們只列出 auto1 部分, auto2 的只要把 stock_name 改成 stock_id 即可. 選項 source 設為回呼函式, 並傳入兩個參數 request 與 response. 但這裡的重點是利用正規表達式物件 RegExt() 來過濾原始資料來源, 其中 "^" 是表示以後面的字串起頭之意. 注意, 因為正規式範本含有變數 term, 所以一定要用 RegExp() 產生範本物件, 沒辦法使用 var reg=/^ ... /i 方式.
var stock_name=["中華電","中碳","台新金","台積電","台塑","創見","創意"];
$("#auto1").autocomplete({source:
function(request, response) {
var term=request.term; //取得使用者輸入字串
var result=new Array();
var reg=new RegExp("^" + term,"i"); //以 term 開頭者
for (var i=0; i<stock_name.length; i++) {
var match=reg.test(stock_name[i]);
if (match) {result.push(stock_name[i]);}
}
response(result);
}
});
接下來我們把範例 8 合而為一改成範例 9, 只用一個 input 元素, 搜尋股名或股號均可 :
股名或股號 : <input gt="" id="auto1" p="" type="text" />
var data=[{label:"中華電", value:"2412"},
{label:"中碳", value:"1723"},
{label:"台新金", value:"3045"},
{label:"台積電", value:"2330"},
{label:"台塑", value:"1301"},
{label:"創見", value:"2451"},
{label:"創意", value:"3443"}];
$("#auto1").autocomplete({source:
function(request, response) {
var term=request.term;
var result=new Array();
var reg=new RegExp("^" + term,"i"); //以 term 開頭者
for (var i=0; i<data.length; i++) {
var match=reg.test(data[i].label) || reg.test(data[i].value);
if (match) {
var obj=new Object();
obj.label=data[i].label + ' (' + data[i].value + ')';
obj.value=data[i].value;
var match=reg.test(data[i].label) || reg.test(data[i].value);
if (match) {
var obj=new Object();
obj.label=data[i].label + ' (' + data[i].value + ')';
obj.value=data[i].value;
result.push(obj);
} //end of if
} //end of for
response(result);
} //end of function
});
測試範例 9 : http://tony1966.xyz/test/jquerytest/autocomplete_9.htm [看原始碼]
以上是以回呼函式處理 (過濾) 本地資料來源的方法. 當然也可以在回呼函式中用 Ajax 從遠端伺服器擷取資料回來後再處理. 不過這樣似乎有點多此一舉, 既然要從遠端伺服器取得資料, 何不將過濾處理的工作全部由伺服器一手包呢 (例如範例 4 我們就將 stock_id 以 term 起頭的過濾作業利用 SQL 完成)? 話是沒錯啦, 如果伺服器程式是自己寫的當然沒問題, 如果不是的話, 例如資料來自於公共資料庫, 你沒辦法要求它回應你要的格式時, 想要呈現客制化格式就要用回呼函式處理了.
我們用範例 10 來測試在回呼函式中用 Ajax 從遠端伺服器擷取資料回來後再處理的作法. 在範例 6 中, 後端 PHP 程式 get_stocks_list_2.php 會回應如下 JSON 資料 :
[{"label":"2451 (創見)","value":"2451"},{"label":"3443 (創意)","value":"3443"}]
現在假定後端程式只能回應如下資料 :
[{"label":"創見","value":"2451"},{"label":"創意","value":"3443"}]
亦即 label 中沒有附帶股號, 而且這是公用資料庫, 回應格式就是固定這樣沒法改, 我們該如何把取得之資料喬成跟上面有附股號的一樣呢? 先把 get_stocks_list_2.php 修改一下變成 get_stocks_list_4.php, 讓它只回應無股號之 JSON 資料 :
$term=$_GET['term']; //擷取 jQueryUI 傳出參數 'term'
$SQL="SELECT * FROM `stocks_list` WHERE `stock_id` LIKE '".$term.
"%' OR `stock_name` LIKE '".$term."%'";
$result=mysql_query($SQL, $conn); //執行 SQL 指令
$arr=Array(); //用來儲存 stock_id 欄位值之陣列
for ($i=0; $i < mysql_numrows($result); $i++) { //走訪紀錄集 (列)
$label=$row["stock_name"];
$value=$row["stock_id"];
$arr[]='{"label":"'.$label.'","value":"'.$value.'"}'; //放入陣列
} //end of for
echo "[".join(",", $arr)."]";
而前端網頁程式部份, 我們就在選項 source 的回呼函式中, 以 Ajax 擷取 get_stocks_list_4.php 的回應資料加以處理, 如範例 10 所示 :
測試範例 10 : http://tony1966.xyz/test/jquerytest/autocomplete_10.htm [看原始碼]
未完待續 ...
8 則留言 :
版主的功力真強
分析能力,很細膩
佩服
版主分析能力,真是細膩
讓人深感佩服
謝謝版主的分享
您過獎了, 我只是按照書上與官網的介紹, 一一測試罷了, 以免日久健忘, 如果能幫上一點忙, 我也很高興 (如果發現謬誤也請告知).
版主您好
近日正也有需求使用到這個功能,
但是在中文情況下卻出不來~~
請問版主,您的soucre裏是否有對中文的部份進行修改,若是有,能否指導小弟一下~~
感謝
已解決,原來是oracle連線時charset沒給。
謝謝版主的分享,獲益良多。
抱歉, 未注意到您有留言, 解決就好.
1.非常感謝版主的教學,一路依照教學測試下來,在下提出1個發現到的錯誤。在測試範例4所張貼的php程式碼有漏。
for ($i=0; $i < $row=mysql_fetch_array($result); //取得列陣列
這一行最後面少了『$i++) {』。
2.不過範例4和5我都沒測試成功,後來才發現是沒有安裝JSON,所以無法使用json_encode()。
3.範例6是可行,小弟依照教學所寫的測試成功。
非常感謝~
怎麼未完待續... 後面就沒有了呢?
另外股名與股號都是單獨的二個欄位,
正常應該是用一個欄位輸入時可同時搜尋股名與股號,User選取後,再把另一個欄位的值也自動帶進去...
如用股名欄位搜尋關鍵字,USER選取後,
股名欄位帶入股名,而股號欄位則自動帶入該股名的股號,這樣才符合
張貼留言