typeahead
這篇文章記錄了我在使用typeahead的一些問題,不是很全,但是基本夠用。
Bootstrap提供typeahead組件來完成自動補全功能。
兩種用法:
直接給標簽添加屬性
<input type="text" data-provide="typeahead">
通過設置autocomplete="off"來關閉瀏覽器自帶的自動補全功能,以防跟我們的產生沖突。
通過JavaScript
調用$('.typeahead').typeahead()
typeahead()詳解
參考官方README和其他一些博客的資料,可以了解typeahead函數可以接受的一些參數,這些參數可以通過指定標簽屬性來傳遞,也可以直接在JavaScript中給出。對於標簽屬性傳遞參數來說,需要在參數前加上data-,例如data-source="".
下表為官方README文檔中參數表格的翻譯版本:
Name | Type | Default | Description |
source | array, function | [] | 用來查詢的數據源。可以是數組或字符串,一個帶有name屬性的JSON對象的數組集合,或者一個函數。函數可以接受兩個參數,query代表輸入框中你的輸入值(即查詢值),process回調函數。The function may be used synchronously by returning the data source directly or asynchronously via the |
items | number | 8 | 下拉選項中出現條目的最大數量。也可以設置為“all” |
minLength | number | 1 | 出發下拉提示的最小長度字符串。可以設置為0,即使沒有填寫任何內容,也會出現提示。 |
showHintOnFocus | boolean or "all" | false | 當輸入框獲得焦點時立刻顯示提示。如果設置為true,顯示所有匹配項。如果設置為“all”,顯示所有提示,並不會按照當前文本過濾。當你需要一個組合框(Combo Box,由文本框和下拉框組成)功能時,可以考慮這個。 |
scrollHeight | number, function | 0 | Number of pixels the scrollable parent container scrolled down (scrolled out the viewport). |
matcher | function | case insensitive | 該函數用來確定匹配條目的規則。接受一個參數,item用於測試查詢字符串是否匹配。通過當前查詢字符串this.query。如果相匹配則返回true |
sorter | function | exact match, case sensitive, case insensitive |
該函數用來對結果進行排序。接受一個參數items並且具有typeahead實例的作用域,通過this.query得到當前查詢。 |
updater | function | returns selected item | 該函數用來返回選中的條目。接受一個item參數並且具有typeahead實例的作用域。 |
highlighter | function | highlights all default matches | 用來高亮自動補全的結果。接受一個item參數並且擁有typeahead實例的作用域。應該返回html |
displayText | function | item.name || item | 用來得到數據源的條目的文本表示。接受一個item參數並且擁有typeahead實例的作用域。應該返回一個字符串。 |
autoSelect | boolean | true | 允許你決定是否自動選擇第一個建議。關閉它意味着如果沒有選擇任何內容(或Enter或Tab),輸入將不會清空。 |
afterSelect | function | $.noop() | 選擇一個條目后的回調函數。It gets the current active item in parameter if any. |
delay | integer | 0 | 在查找之間添加延遲 |
appendTo | jQuery element | null | 默認情況下,菜單將會出現在輸入元素的之后。使用這個選項來添加菜單到其他div。如果你想使用bootstrap的dropup或者dropdown-menu-right classes,就不要使用它 |
fitToElement | boolean | false | 如果你希望菜單的大小與其所鏈接的輸入的大小相同,置為true |
addItem | JSON object | false | 在list的最后添加一個條目,例如“New Entry”。這可能被用到,例如當一個條目在數據集中沒有被找到的時候,彈出對話框。 |
示例代碼
使用靜態的內容網上資料很多,直接給source就可以了,更加普遍的需求是:通過aJax動態的查詢。
ajax動態查詢
$("#search-platform").typeahead({
source: function (query, process) {
return $.ajax({
url: '/domain/xxxx',
type: 'post',
data: {name: query},
success: function (result) {
return process(result);
},
});
}
});
這里我們只使用了一個source選項,指定一下數據源,通過上表我們了解到source可以是一個帶有兩個參數的函數,其兩個參數分別為query和process,query是當前輸入框中所輸入的內容,process是一個回調函數,它用來將我們得到的結果轉換為typeahead組件可以識別的數據。結合上面一段代碼,我們返回的內容是通過ajax請求得到的結果,但是這個結果經過了process進行處理。
ajax動態查詢,定制顯示內容
上面的需求很簡單,但是有的時候要考慮更加完善的情況,比方說現在有一個查詢框,是用來查詢學生姓名的,考慮問題:
學生存在重名情況,我們具體想查詢的是學號為001的叫張三的學生,但我們不能輸入001,需求就是輸入張三,而且查詢的時候,一定是根據學號(主鍵)來查詢的,那么我們可以這樣顯示:
但是如何顯示成這種形式,並且我們如何獲取后面的學號呢?
1.首先,ajax所訪問的后台接口就不能只返回name的字符串數組,而應該返回對象。返回一組Student對象,並且以JSON字符串形式返回。這樣前段所接受的內容就是一個JSON對象的集合
2.但是在顯示上是有問題的,它將顯示為:
我們可以發現幾個問題:1.顯示格式為JSON字符串,2.如果Student類含有其它屬性,那么也會顯示出來,除非我們為了這個需求重新設計一個簡單對象。
現在考慮解決這幾個問題:
1.只保留需要顯示/使用的字段
2.顯示格式正確,不要JSON格式
3.只能根據姓名檢索
source
對於第一個問題,我們需要對數據做預處理,即對ajax獲取的數據做處理:
success: function (result) {
var resultList = result.items.map(function (item) {
var aItem = {id: item.id, name: item.name};
return JSON.stringify(aItem);
});
return process(resultList);
}
result是接口返回的JSON數據,我的數據被一個items對象所包裹,所以是result.items.map。
highlighter
前面了解到,highlighter是用來高亮匹配項的,這個高亮可以使用我們自己想要的風格。方法的原始實現也是高亮匹配內容,但是這里不適用,我們希望只顯示name:
/** * 使用指定的方式,高亮(指出)匹配的部分 * * @param obj 數據源中返回的單個實例 * @returns {XML|void|string|*} 數據列表中數據的顯示方式(如匹配內容高亮等) */ highlighter: function (obj) { var item = JSON.parse(obj); var query = this.query.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&'); return item.name.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) { return '<strong>' + match + '</strong>' }); }
這里反回了html文本,加粗匹配項。這樣顯示出來的內容就是只有name的情況。但這還不夠,你會發現當你選中了一個條目,填充到輸入框的內容還是JSON格式。我們還需要重寫updater方法:
updater
/** * 在選中數據后的操作,這里的返回值代表了輸入框的值 * * @param obj * @return 選中后,最終輸入框里的值 */ updater: function (obj) { var item = JSON.parse(obj); return item.name; }
最終輸入框中我們只讓他回填name。
這樣最終的顯示內容就符合了需求,但是當我們提交表單進行查詢的時候,還是沒有id這個值,這里可能需要一個隱藏域來存放id,我們在updater中將隱藏域的value設置為item.id就可以了。