http://ju.outofmemory.cn/entry/155452
前言
存在這么個需求: 有個輸入框,他要智能提示輸入。然后,我找到了這個js - typeahead.js 。然后,開始研究一下,如何去使用。
簡介
靈感來自twitter的自動補全搜索功能,typeahead.js是實現預輸入的強大靈活的javascript庫。Typeahead.js庫包含了兩個組件:推薦引擎 Bloodhound 以及UI視圖 typeahead 。
typeahead的項目的目錄結構如下:
- dist/ : 項目分發包的目錄
- doc/ : 文檔目錄
- src/ : 源碼目錄
- test/ : 測試目錄
- .jshintrc : ??
- .travis.yml : travis-ci服務, github上的開源項目使用的持續構建服務
- bower.json : bower工具的配置文件
- CHANGELOG.md : 更改日志
- composer.json : 項目的組成信息
- Gruntfile.js : 統一項目管理
- karma.conf.js : 自動化單元測試
- package.json : 類似Gemfile的,項目的包依賴
- typeahead.js.jquery.json : 子項目包依賴
- typeahead項目中樣例在其
gh-pages
分支中。
Node小知識: Jasmine做單元測試,Karma自動化完成單元測試,Grunt啟動Karma統一項目管理,Yeoman最后封裝成一個項目原型模板,npm做nodejs的包依賴管理,bower做javascript的包依賴管理
推薦引擎負責對給定的查詢計算推薦結果,UI視圖渲染推薦並處理DOM交互。這兩者都可分開使用,但是,結合使用可以提供豐富的預輸入的體驗。以下,分別介紹兩者。
Bloodhound
Bloodhound是typeahead.js的推薦引擎,健壯且靈活,而且還提供諸如預取,智能緩存,快速查找,遠程數據回填。下面,從特性和使用方面分別介紹,使用則從API、選項、預取等方面介紹。
其特性如下:
- 可使用硬編碼數據
- 初始化是預取數據,從而減少推薦時延
- 智能使用本地存儲從而減少網絡請求
- 從遠程源中回填建議項
- 對遠程請求的速率限制並緩存網絡請求,從而減輕負載
具體的使用,按API,選項,數據集,定制事件和 Look and Feel介紹。
API
new Bloodhound(options)
是構造函數,其接受哈希選項作為唯一的參數。
var engine = new Bloodhound({ name: 'animals', local: [{ val: 'dog' }, { val: 'pig' }, { val: 'moose' }], // 本地數據源 remote: 'http://example.com/animals?q=%QUERY', // 遠程數據源 datumTokenizer: function(d) { return Bloodhound.tokenizers.whitespace(d.val); }, queryTokenizer: Bloodhound.tokenizers.whitespace });
Bloodhound#initialize(reinitialize)
啟動了推薦引擎。其啟動過程包括處理有local
以及prefetch
提供的數據。初始化完成前,其他方法均不起作用; 初始化完成后,返回一個 jQuery promise 。
var promise = engine.initialize(); promise .done(function() { console.log('success!'); }) .fail(function() { console.log('err!'); });
After the initial call of initialize
, how subsequent invocations of the method behave depends on the reinitialize
argument. If reinitialize
is false, the method will not execute the initialization logic and will just return the same jQuery promise returned by the initial invocation. If reinitialize
is truthy, the method will behave as if it were being called for the first time.
在 initialize
調用之后,后續調用方法的行為依賴於 reinitialize
參數。如果 reinitialize
為false,函數不執行初始化邏輯,並僅僅返回和最初調用相同的jQuery promise。 如果 reinitialize
為真,初始化就重新執行。
var promise1 = engine.initialize(); var promise2 = engine.initialize(); var promise3 = engine.initialize(true); promise1 === promise2; // 相等性判斷 promise3 !== promise1 && promise3 !== promise2;
Bloodhound#add(datums)
獲取一個數據參數datums,其中的數據將被添加到搜索索引中,從而增強推薦引擎。
engine.add([{ val: 'one' }, { val: 'two' }]);
Bloodhound#clear()
從搜索引擎中移除所有推薦。
engine.clear();
Bloodhound#clearPrefetchCache()
如果使用了prefetch
選項,數據將會緩存到本地存儲中,從而減少不必要的網絡請求。clearPrefetchCache
提供了可編程移除緩存的方法。
engine.clearPrefetchCache();
Bloodhound#clearRemoteCache()
如果使用了remote
選項,Bloodhound將會緩存最近10條響應,從而提供最佳的用戶體驗。clearRemoteCache
提供編程清除緩存的方法。
engine.clearRemoteCache();
Bloodhound.noConflict()
返回Bloodhound構造器的引用,並返回window.Bloodhound
的先前的值。可以用來避免命名沖突。
var Dachshund = Bloodhound.noConflict();
Bloodhound#get(query, cb)
為query
計算一組建議。cb
為之前提到datums。cb
總是與在客戶端可用的建議一起同步調用。如果客戶端的不夠,將會考慮remote
。cb
也可以異步的方式,混合着客戶端和remote
端數據進行調用。注: 例子中顯示,cb
為函數。
bloodhound.get(myQuery, function(suggestions) { suggestions.each(function(suggestion) { console.log(suggestion); }); });
Options
當實例化一個Bloodhund推薦引擎時,存在如下的選項可配置:
datumTokenizer
– 將datum轉換為字符串token的數組的函數,其簽名為(datum)
。 必須queryTokenizer
– 將查詢轉換為字符串token的數組的函數,其簽名為(query)
。 必須limit
–Bloodhound#get
方法返回的最大的建議數目. 如果不夠,就用遠程的數據回填。默認為5個。dupDetector
– 如果設置該選項,其值為帶有(remoteMatch, localMatch)
選項的冗余檢測函數,即數據中存在冗余,則返回為true,否則返回為false。如果不設置,則不執行冗余檢測。sorter
– 用來為給定查詢所匹配的數據進行排序。local
– datums數組或返回datums數組的函數。prefetch
– 可以是包含datums數組的JSON文件的URL,更多信息,參考[prefetch]哈希選項。remote
– 當local
和prefetch
提供的數據不充分,或需要更多的可配置性時,用來從遠程獲取建議的URL。
Prefetch
在初始化過程中,預取並處理數據。如果瀏覽器支持本地存儲,處理數據將被緩存,從而避免后續頁面加載中額外的網絡請求。
警告 盡管小數據集可以不用預取緩存,並且預取不意味着包含整個數據集。相反的,需將其看作推薦的一級緩存。如果不謹記在心,可能會觸及本地緩存限制。
當配置 prefetch
時,如下選項亦可用:
url
– 鏈接到包含datums數組的JSON文件的URL。 必須cacheKey
– 存儲在本地的數據的key。默認為url
的值ttl
– 預取的數據緩存在本地存儲中的時間(毫秒),默認為86400000
(1天)thumbprint
– 用作預取數據的數據指紋。如果和本地緩存的數據不匹配,數據將會被重新獲取filter
– 將響應體轉換成datums的數組的函數,其簽名為filter(parsedResponse)
,返回值為datums數組。ajax
– 傳遞給jQuery.ajax
的ajax設置
Remote
遠程數據僅在 local
和 prefetch
提供的數據不充分時,才會使用。為了避免到遠程端過於繁瑣的請求數,請求被限速了。
當配置 remote
時,如下的選項是可用的:
url
– 當local
和prefetch
的數據不足時,發出請求的URL。 必須wildcard
-url
中的模式,當發出請求時,將會被替換為用戶的查詢。默認為%QUERY
replace
– 用來覆蓋請求URL的函數, 其方法簽名為replace(url, query)
,返回值為一合法的URL。如果設置,將不對URL執行任何替換?rateLimitBy
– 該方法用來實時顯示網絡請求數目。其值為debounce
或throttle
,默認為debounce
。rateLimitWait
–rateLimitBy
所使用的時間間隔(毫秒),默認為300
filter
– 將響應體轉換為數組datums的函數,其方法簽名為filter(parsedResponse)
,返回值為datums數組ajax
– 傳給jQuery.ajax
ajax設置對象
Datums
Datums are JavaScript objects that hydrate the pool of possible suggestions. Bloodhound doesn’t expect datums to contain any specific properties as any operations performed on datums are done using functions defined by the user i.e.datumTokenizer
, dupDetector
, and sorter
.
Tokens
The algorithm used by bloodhounds for providing suggestions for a given query is token-based. When Bloodhound#get
is called, it tokenizes query
using queryTokenizer
and then invokes cb
with all of the datums that contain those tokens.
For a quick example, if a datum was tokenized into the following set of tokens…
['typeahead.js', 'typeahead', 'autocomplete', 'javascript'];
…it would be a valid match for queries such as:
typehead
typehead.js
autoco
java type
typeahead
typeahead.js的UI組件是一個jQuery插件,其負責渲染建議並處理DOM交互。其特性如下:
- 在用戶輸入時,顯示建議
- 將頂層推薦顯示為hint(例如,背景文字)
- 支持可定制的模板
- 同RTL語言和輸入編輯器工作良好
- 高亮匹配的查詢
- 觸發定制事件
規范
為了最大程度的利用用戶現有的關於typeahead.js的知識,typeahead.js UI的行為是仿照谷歌的搜索框。下面的偽代碼,介紹UI界面如何處理相關事件的。
預輸入需要考慮的事件有: 聚焦是失焦,值的改變,方向鍵,Tab鍵,Esc鍵
輸入控件聚焦
activate typeahead //激活typeahead
輸入控件失去焦點
deactivate typeahead //失效typeahead close dropdown menu //關閉下拉菜單 remove hint //移除hint clear suggestions from dropdown menu //清理下拉菜單中的推薦
輸入控件中的值的改變
IF query satisfies minLength requirement THEN //查詢滿足最小長度需求 request suggestions for new query //為新的查詢請求推薦 IF suggestions are available THEN render suggestions in dropdown menu open dropdown menu update hint ELSE close dropdown menu clear suggestions from dropdown menu remove hint ENDIF ELSE close dropdown menu clear suggestions from dropdown menu remove hint ENDIF
按下Up鍵
IF dropdown menu is open THEN move dropdown menu cursor up 1 suggestion ELSE request suggestions for current query IF suggestions are available THEN render suggestions in dropdown menu open dropdown menu update hint ENDIF ENDIF
按下Down鍵
IF dropdown menu is open THEN move dropdown menu cursor down 1 suggestion ELSE request suggestions for current query IF suggestions are available THEN render suggestions in dropdown menu open dropdown menu update hint ENDIF ENDIF
左箭頭按下
IF detected query language direction is right-to-left THEN IF hint is being shown THEN IF text cursor is at end of query THEN autocomplete query to hint ENDIF ENDIF ENDIF
右箭頭按下
IF detected query language direction is left-to-right THEN IF hint is being shown THEN IF text cursor is at the end of the query THEN autocomplete query to hint ENDIF ENDIF ENDIF
按Tab鍵
IF dropdown menu cursor is on suggestion THEN close dropdown menu update query to display key of suggestion remove hint ELSIF hint is being shown THEN autocomplete query to hint ENDIF
Enter按鍵
IF dropdown menu cursor is on suggestion THEN close dropdown menu update query to display key of suggestion remove hint prevent default browser action e.g. form submit ENDIF
Esc按鍵
close dropdown menu remove hint
點擊推薦項
update query to display key of suggestion close dropdown menu remove hint
API
jQuery#typeahead(options, [\*datasets])
Turns any input[type="text"]
element into a typeahead. options
is an options hash that’s used to configure the typeahead to your liking. Refer to Options for more info regarding the available configs. Subsequent arguments (*datasets
), are individual option hashes for datasets. For more details regarding datasets, refer to Datasets .
將任何 input[type="text"]
元素轉換成typehead。 options
參數hash將typeahead配置為你所喜歡的, *datasets
參數是配置數據集的獨立選項hash。更多關於選項的信息參考下文。
$('.typeahead').typeahead({ minLength: 3, highlight: true, }, { name: 'my-dataset', source: mySource });
jQuery#typeahead('destroy')
移除typeahead功能,並將input
元素的狀態重置為原始狀態。
$('.typeahead').typeahead('destroy');
jQuery#typeahead('open')
打開typeahead下拉菜單。 注意,打開菜單不意味着菜單可見。僅當其打開並存在內容時,菜單才可見。
$('.typeahead').typeahead('open');
jQuery#typeahead('close')
關閉typeahead的下拉菜單。
$('.typeahead').typeahead('close');
jQuery#typeahead('val')
返回typeahead的當前值,該值為用戶輸入到input
元素中的文本。
var myVal = $('.typeahead').typeahead('val');
jQuery#typeahead('val', val)
設置typeahead的值,要來替代jQuery#val
函數。
$('.typeahead').typeahead('val', myVal);
jQuery.fn.typeahead.noConflict()
返回typeahead插件的引用,並將jQuery.fn.typeahead
重置為先前值。這可以用來避免命名沖突。
var typeahead = jQuery.fn.typeahead.noConflict(); jQuery.fn._typeahead = typeahead;
選項(Options)
當初始化typeahead時,存在如下的可配置的選項:
highlight
– 設置為true
時,當建議渲染時,在文本節點中,匹配查詢模式的文字將被帶有tt-highlight
class的strong
元素包裹。默認設置為false
。hint
– 設置為false
時,typeahead 將不會顯示hint。默認為true
.minLength
– 推薦引擎開始渲染所需要的最小字符。默認為1
.
數據集(Datasets)
typeahead
可以由一個或多個數據集組成。但用戶修改typeahead的值時,每個數據集都會嘗試渲染為新的查詢渲染值。
大多數情況下,一個數據集足夠了。只有在需要在下拉菜單中,以某些分類關系分組渲染推薦時,才需要使用多個數據源。例如,在 twitter.com
中,搜索預輸入將結果分組為相關搜索,趨勢,賬戶 - 這就需要使用多個數據集。
數據集可以通過如下的選項進行配置:
-
source
– 推薦的數據源支持。值為帶有(query, cb)
簽名的函數。該函數將會計為query
計算推薦集,然后以計算的推薦集調用cb
。函數cb
的調用可以是同步的,也可以是異步的。Bloodhound推薦引擎可在這里使用,更多參考[Bloodhound Integration]。 必須 -
name
– 數據集的名字。該名字可以被追加到tt-dataset-
,從而形成包含DOM元素的類名。只能由下划線、-,字母和數字組成。默認為隨機數。 displayKey
– 對於一個給定的推薦對象,決定其的字符串表示,並將會在某個輸入控件選擇后使用。其值可以是關鍵字符串,或者是將推薦對象轉換為string的函數。默認為value
。-
templates
– 渲染數據集使用的哈希模板。注意:預編譯的模板是將javascript對象作為第一參數,並返回為HTML字符串。-
empty
– 當給定查詢推薦數為0時,渲染empty
中的內容。empty
的值可以是HTML字符串或預編譯模板。如果是預編譯模板,其內容中將包含query
。 -
footer
– 數據集底部渲染的內容,可為HTML字符串或預編譯模板。如果是預編譯模板,其中包含query
和isEmpty
。 -
header
– 數據集頭部渲染的內容,可為HTML字符串或預編譯模板。如果是預編譯模板,其中包含query
和isEmpty
。 -
suggestion
– 用來渲染單個推薦。其值必須是預編譯模板。其中包含關聯的建議對象。默認為將displayKey
包裝在p
標簽中:<p></p>
-
Custom Events
typeahead組件觸發了如下的定制的事件:
typeahead:opened
– 當typeahead的下拉菜單打開時觸發。typeahead:closed
– 當typeahead的下拉菜單關閉時觸發。typeahead:cursorchanged
– 但下拉菜單的光標移動到另一個推薦時,觸發事件。事件處理器將接受三個參數: jQuery事件對象、推薦對象以及推薦對象所屬的數據集名。typeahead:selected
– 當下拉菜單被渲染時觸發。事件處理器接受三個參數: jQuery事件對象,推薦對象以及推薦對象所屬的數據集名。typeahead:autocompleted
– 當查詢自動補全時觸發。自動補全意味着將hint
改變為查詢,事件器將調用3個參數: jQuery事件對象、推薦對象以及推薦對象所屬的數據集名。
All custom events are triggered on the element initialized as a typeahead.
所有的定制事件將會在元素初始化為typeahead時觸發。
Look and Feel
下面是虛構的mustache模板,用來描述typeahead下拉菜單DOM元素結構。 注意, header
, footer
, suggestion
以及 empty
都是有datase提供的模板。
<span class="tt-dropdown-menu"> <div class="tt-dataset-"> } <span class="tt-suggestions"> <div class="tt-suggestion">}</div> } </span> } </div> </span>
當用戶在 .tt-suggestion
的a鏈接上移動鼠標或鍵盤時,將會追加樣式 tt-cursor
。可使用該樣式類標識位於光標下的推薦。
Bloodhound Integration
由於數據集期望 source
屬性是一個函數,所以,不能直接將Bloodhound推薦引擎傳遞進來。相反的,需要使用推薦引擎的typeahead適配器 ttAdapter
。
var engine = new Bloodhound({ /* options */ }); engine.initialize(); $('.typeahead').typeahead(null, { displayKey: myDisplayKey // if not set, will default to 'value', source: engine.ttAdapter() });
實踐
教程已看完,求實踐。其實typeahead項目中包含例子,但是,起初沒想到其包含其項目的gh-pages分支中。目前,正在研究其提供的例子。
首先,例子中里使用的css庫: normalize.min.css,用到的js的庫:
- handlebars.js : Mustache模板語言擴展的編譯器,hogan.js是Mustache的編譯器
- jquery-1.10.2.min.js : typeahead.jquery.js依賴jquery
- ghostwriter.min.js : 提供簡單的API,從而與
input[type="text"]
交互。給定特定元素,設置選項。 - typeahead.bundle.js : typeahead.jquery.js和bloodbound.js的文件組合。
實踐參考tagmanger.js中的例子,使用的整體感覺不是特別的好。
備注: 由於bootstrap 3.x 放棄了typeahead.js,因而,衍生出一些過度的項目:
小結
看完之后,覺得預輸入這種東西,還真是強大的不得了啊,需要監聽的事件不少啊。自己曾結合jquery ui的autocomplete的實現實在是太挫了,依賴的東西太多了。但是,怎么將其應用到網站上,貌似還有些距離。