BootstrapTagsInput是一個基於jQuery和Bootstrap.css的用於管理標籤的插件。javascript
官網在這:官網php
這個官網呢,怎麼說呢,比較簡潔。示例聊勝於無。css
最簡單的用法就是在引入jquery,和Bootstrap的前提下,在input標籤中添加 data-role="tagsinput",便可初始化。html
<input type="text" value="" data-role="tagsinput" />
緣由在於下方代碼,代碼最後一部分,寫了是如何初始化的。前端
/** * Initialize tagsinput behaviour on inputs and selects which have * data-role=tagsinput */ $(function() { $("input[data-role=tagsinput], select[multiple][data-role=tagsinput]").tagsinput(); });
還有一個經過 select標籤初始化的方式。並在標籤加上 multiple 屬性,在經過$("select").tagsinput('items'),獲取值時,返回的是一個數組,而不是逗號分隔的字符串了。java
<select multiple data-role="tagsinput"> <option value="Amsterdam">Amsterdam</option> <option value="Washington">Washington</option> <option value="Sydney">Sydney</option> <option value="Beijing">Beijing</option> <option value="Cairo">Cairo</option> </select>
以上兩個方法都會將現有value值或者option元素,自動設置爲標籤。jquery
通常來講,若是你只使用輸入,回車成爲標籤的功能,那你可能遇不到什麼坑。git
可是一旦你遇到了根據用戶輸入顯示相關補全/提示/預輸入的需求時,坑就來了。github
在其文檔中,有一個介紹,就是關於Typeahead的,他是這麼說的:web
Typeahead is not included in Bootstrap 3, so you'll have to include your own typeahead library. I'd recommed typeahead.js. An example of using this is shown below.
大意是:Bootstrap 3中已經不包含Typeahead啦,若是你想用,你必須本身引入的Typeahead庫。
咱們推薦了typeahead.js。下面是使用此示例的示例。巴拉巴拉。
<input type="text" value="Amsterdam,Washington" data-role="tagsinput" /> <script> var citynames = new Bloodhound({ datumTokenizer: Bloodhound.tokenizers.obj.whitespace('name'), queryTokenizer: Bloodhound.tokenizers.whitespace, prefetch: { url: 'assets/citynames.json', filter: function(list) { return $.map(list, function(cityname) { return { name: cityname }; }); } } }); citynames.initialize(); $('input').tagsinput({ typeaheadjs: { name: 'citynames', displayKey: 'name', valueKey: 'name', source: citynames.ttAdapter() } }); </script>
-------------------------------------------------------------------------------------------------------------------------------------------------------------
而後在其文檔下方有個Options介紹表,裏面有一項關於typeahead的介紹,其中關於source的介紹是:
An array (or function returning a promise or array), which will be used as source for a typeahead.
大意是:一個數組(或返回一個承諾或數組的函數),它將用typeahead的數據源。
說實話,tagsinput 這文檔給我一種什麼感受呢?
就是=>饒你奸似鬼!照樣喝老孃的洗腳水!
上面說了,他給了一個推薦的Typeahead示列,而後下方的option表中有其對應的描述。
若是你只是直接複製示例代碼,直接運行,你會遇到別的坑,可是我要說的是,睜大眼睛,仔細觀瞧!
在初始化tagsinput時有兩個預輸入選項,一個是typeaheadjs,另外一個是typeahead。
看出區別了嗎?區別多了一個js!!!!!
typeahead.js
Inspired by twitter.com's autocomplete search functionality, typeahead.js is a flexible JavaScript library that provides a strong foundation for building robust typeaheads.The typeahead.js library consists of 2 components: the suggestion engine, Bloodhound, and the UI view, Typeahead. The suggestion engine is responsible for computing suggestions for a given query. The UI view is responsible for rendering suggestions and handling DOM interactions. Both components can be used separately, but when used together, they can provide a rich typeahead experience.
這裏說明了,typeaheadjs是受twitter.com自動完成搜索功能的啓發,獨立的庫。
typeahead.js庫由2個組件組成:建議引擎, Bloodhound和UI視圖,Typeahead。
而後按照tagsinput的示例代碼去作。能夠作出來。
可是例子是你顯示什麼!值就是什麼!就是說你顯示「大腦虎」,你選擇輸入「大腦虎」成爲標籤,你提交的值也是「大腦虎」這個字符串!
通常狀況下,用於搜索功能沒啥問題,由於搜的就是字符串,可是若是用於其餘地方,實際值id和顯示值name分離的怎麼辦??
例如城市選擇:
[{id: 101, name: "北京"},{id: 102, name: "南京"}]
給用戶展現的是「北京」,「南京」的字符串,實際上傳遞給後臺入庫的實際上是「id」的值。
示例中的初始化是這樣的:
$('input').tagsinput({ typeaheadjs: { name: 'citynames', displayKey: 'name',// valueKey: 'name',//你把這個name換成ID他也不正常 source: citynames.ttAdapter() } });
注意啊,這個displayKey和valueKey 設置的是typeaheadjs 的顯示和實際值,和tagsinput沒有關係,顯示依然不正確!
須要引入的文件:
<link rel="stylesheet" href="./bootstrap-tagsinput.css" /> <script src="./jquery-3.2.1.js"></script> <script src="./typeaheadjs.js"></script> <script src="./bootstrap-tagsinput.js"></script>
提示欄的下拉樣式:
<style> .label { background: #428bca; } .bootstrap-tagsinput .tag { color: #000; } .twitter-typeahead.tt-query, .twitter-typeahead .tt-hint { margin-bottom: 0; } .twitter-typeahead .tt-hint { display: none; } .tt-menu { position: absolute; top: 100%; left: 0; z-index: 1000; display: none; float: left; min-width: 160px; padding: 5px 0; margin: 2px 0 0; list-style: none; font-size: 14px; background-color: #ffffff; border: 1px solid #cccccc; border: 1px solid rgba(0, 0, 0, 0.15); border-radius: 4px; -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); background-clip: padding-box; cursor: pointer; } .tt-suggestion { display: block; padding: 3px 20px; clear: both; font-weight: normal; line-height: 1.428571429; color: #333333; white-space: nowrap; } .tt-suggestion:hover, .tt-suggestion:focus { color: #ffffff; text-decoration: none; outline: 0; background-color: #428bca; } </style>
HTML:
<body> <div class="example"> <h2>基本示例</h2> <form action="#" method="get"> <input data-ajax="test.php" class="typeahead" type="text" name="test" placeholder="請輸入省份" value="" /> <button type="submit">提交</button> </form> </div> </body>
PHP後端:
<?php echo json_encode([["id"=>101,"name"=>"北京"],["id"=>102,"name"=>"南京"]]); ?>
PHP注意事項:
一、新建一個名叫「test.php」的文件,把這個粘貼進去,放在與你script的同級目錄下。
二、要在服務器端上運行(本地的),且安裝了PHP。
js:
function tagsinputAjax(inputSelector, valueName, showName) { var $input = $(inputSelector), ajaxUrl = $input.attr("data-ajax"); //初始化bh var engine = new Bloodhound({ queryTokenizer: Bloodhound.tokenizers.whitespace, datumTokenizer: Bloodhound.tokenizers.whitespace, remote: { url: ajaxUrl + "?q=%QUERY", wildcard: "%QUERY" } }); engine.initialize(); var typeaheadConfig = { name: valueName, // displayKey: valueName, // valueKey: showName, templates: { suggestion: function (data) { //格式化預輸 console.log(data) return '<div data-id="' + data[valueName] + '">' + data[showName] + '</div>'; } }, source: engine.ttAdapter() }; //初始化 $input.tagsinput({ itemValue: valueName, itemText: showName, typeaheadjs: typeaheadConfig }) } tagsinputAjax(".typeahead", "id", "name");
這段作了一個簡單的封裝,指定了class爲「.typeahead」的input初始化爲tags-input,顯示的值爲「name」,實際值爲「id」。
注意事項:
1.此例子爲後臺處理查詢值,前端get方式獲取類似值。
2.數據爲動態獲取,非一次性獲取所有數據,使用建議引擎去提示(tagsinput官網給的是所有的獲取過來的)方式,而是每次輸入的提示都從後臺獲取,我作的時候總數據有3w條,一次性加載過來顯然是不合適的。
3.typeaheadjs會緩存數據的,因此在調試的時候要注意,若是未按照預期呈現結果,記得清緩存後再次調試。
4.註釋掉的displayKey 和 valueKey不會有影響,是由於格式化了suggestion條目。
5.data-ajax="test.php"寫在了input上,由於我實際作項目時,表單頁面是後臺動態生成的,根據不一樣表單設置不一樣的data-ajax能夠獲取不一樣預輸入數據。
介紹就不貼了,本身看吧。
Bootstrap 3 Typeahead 原來是集成在Bootstrap 2裏的一個js插件,到3的時候給獨立出來了。
直接翻到GitHub官網最下面,就是介紹Bootstrap Tags Input如何使用Bootstrap 3 Typeahead。
Bootstrap Tags Input is a jQuery plugin providing a Twitter Bootstrap user interface for managing tags. Bootstrap Tags Input has a typeahead option which allows you to set the source:
$("input").tagsinput({ typeahead: { source: ["Amsterdam", "Washington", "Sydney", "Beijing", "Cairo"] } });or
$("input").tagsinput({ typeahead: { source: function(query) { return $.get("http://someservice.com"); } } });See also: https://github.com/bassjobsen/Bootstrap-3-Typeahead/issues/40
本地的預輸入沒有問題,有問題的是第二個,你把$.get中的地址換成你本身以後,它請求成功了,可是!它沒有提示!它沒有顯示預輸入的下拉。
這是爲啥呢?我也不知道。
問題出現了!那就想辦法解決吧。
首先,我把代碼由:
$("input").tagsinput({ typeahead: { source: function (query) { return $.get("test.php?q=" + query); } } });
改成:
$("input").tagsinput({ typeahead: { source: function (query) { // return $.get("test.php?q=" + query); return ["北京", "南京"]; } } });
結果發現可用,那說明什麼?
說明:
1.請求時成功的,而且是返回數據的,那麼請求的數據沒有問題。
2.直接return城市數據組,依然可使用,說明不是數據格式的問題。
3.問題出在了$.get()這了。
說明是返回數據的問題,我把數據返回一下,寫成這樣是否是就好了?
$("input").tagsinput({ typeahead: { source: function (query) { var result=null; $.ajax({ url: "test.php?q=" + query, type: "get", dataType: "json",//對的,數據類型不能少 success: function (res) { result = res.data; } }); return result; } } });
而後報錯了...
Uncaught TypeError: Cannot read property 'success' of null bootstrap-tagsinput.js:294
at Typeahead.source (bootstrap-tagsinput.js:294)
at Typeahead.<anonymous> (bootstrap3-typeahead.js:226)
查了一下 ,截取一下tagsinput.js
...... source: function (query, process) { function processItems(items) { var texts = []; for (var i = 0; i < items.length; i++) { var text = self.options.itemText(items[i]); map[text] = items[i]; texts.push(text); } process(texts); } this.map = {}; var map = this.map, data = typeahead.source(query);// 這裏的data就是返回的數據 if ($.isFunction(data.success)) {//報錯的是這裏 // support for Angular callbacks data.success(processItems); } else if ($.isFunction(data.then)) { // support for Angular promises data.then(processItems); } else { // support for functions and jquery promises $.when(data) .then(processItems); } }, ......
打印了一下data爲null,因此報錯。爲啥呢?爲返回的數據是null。network裏顯示xhr請求時成功的啊,也返回了數據啊?
其實改一下代碼就行了:
$("input").tagsinput({ typeahead: { source: function (query) { var result=null; $.ajax({ url: "test.php?q=" + query, type: "get", dataType: "json",//對的,數據類型不能少 async: false,//新增了一個async爲false success: function (res) { result = res.data; } }); return result; } } });
而後就OK了。爲啥這就OK了呢?本身想一下,很簡單的。
若是仍然想用顯示和值分離,則以下:
$("input").tagsinput({ itemValue: '此處填寫實際的要傳遞給後臺的key', itemText: '此處填寫顯示給用戶看的的key', typeahead: { source: function (query) { var result=null; $.ajax({ url: "test.php?q=" + query, type: "get", async: false, success: function (res) { result = res.data; } }); return result; } } });
php:
<?php echo json_encode(["status" => "OK", "data" => [ ["id"=>101,"name"=>"北京"], ["id"=>102,"name"=>"南京"] ]]) ?>
注意:
我此次作項目用到了tagsinput.js,這貨的手冊作的特別的不友好,示列也不許確。基本上初次用的人都會遇到各類各樣的問題,我爲了解決各類問題各類google。心累,不愛。
typeahead和typeaheadjs傻傻分不清楚。
tagsinput.js源碼中有註釋,看到沒,它去判斷設置中用的是Bootstrap 3 Typeahead的預輸入仍是用的typeaheadjs。
更操蛋的是,它重寫了
Bootstrap tagsinput use the source parameter only. When using this
typeahead
option bootstrap tagsinput initiates a newtypeahead
class:self.$input.typeahead
. Bootstrap tagsinput's typeahead classes overwrite the original source function and has a call to process already as shown below:
Bootstrap tagsinput僅使用source參數。使用此typeahead
選項時,bootstrap tagsinput會啓動一個新typeahead
類:self.$input.typeahead
。Bootstrap tagsinput的typeahead類覆蓋原始源函數,而且已經調用process,以下所示:
if (self.options.typeahead) { var typeahead = self.options.typeahead || {}; makeOptionFunction(typeahead, 'source'); self.$input.typeahead($.extend({}, typeahead, { source: function (query, process) { function processItems(items) { var texts = []; for (var i = 0; i < items.length; i++) { var text = self.options.itemText(items[i]); map[text] = items[i]; texts.push(text); } process(texts); } this.map = {}; var map = this.map, data = typeahead.source(query); if ($.isFunction(data.success)) { // support for Angular callbacks data.success(processItems); } else if ($.isFunction(data.then)) { // support for Angular promises data.then(processItems); } else { // support for functions and jquery promises $.when(data) .then(processItems); } }, updater: function (text) { self.add(this.map[text]); return this.map[text]; }, matcher: function (text) { return (text.toLowerCase().indexOf(this.query.trim().toLowerCase()) !== -1); }, sorter: function (texts) { return texts.sort(); }, highlighter: function (text) { var regex = new RegExp( '(' + this.query + ')', 'gi' ); return text.replace( regex, "<strong>$1</strong>" ); } })); }
生活啊 !坑爹啊!