Twitter bootstrap typeahead multiple values?
Solution 1
Edit There already was a pull about that : https://github.com/twitter/bootstrap/pull/2007
You can approach the desired behavior by using a proxy for the typeahead : Demo (jsfiddle)
var $myTextarea = $('#myTextarea');
$('.typeahead').typeahead({
source: source,
updater: function(item) {
$myTextarea.append(item, ' ');
return '';
}
});
I think the updater
method is meant for this kind of thing, you just return what will be displayed.
Or if you really want everything to be in the same input element, you would have to override more methods so that it only matches the currently-typed element : Demo (jsfiddle)
function extractor(query) {
var result = /([^,]+)$/.exec(query);
if(result && result[1])
return result[1].trim();
return '';
}
$('.typeahead').typeahead({
source: source,
updater: function(item) {
return this.$element.val().replace(/[^,]*$/,'')+item+',';
},
matcher: function (item) {
var tquery = extractor(this.query);
if(!tquery) return false;
return ~item.toLowerCase().indexOf(tquery.toLowerCase())
},
highlighter: function (item) {
var query = extractor(this.query).replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&')
return item.replace(new RegExp('(' + query + ')', 'ig'), function ($1, match) {
return '<strong>' + match + '</strong>'
})
}
});
This one is not idiot proof, because you have to type at the end, after the special character.
Solution 2
This is an excellent replacement for select boxes:
http://ivaynberg.github.io/select2/
(If you use the multi-value version.)
Solution 3
The top answer doesn't seem to work anymore with the latest typeahead, so I offer the following.
function MultiTypeahead(id, data, trigger, vertAdjustMenu)
{
trigger = (undefined !== trigger) ? trigger : '';
var validChars = /^[a-zA-Z]+$/;
function extractor(query)
{
var result = (new RegExp('([^,; \r\n]+)$')).exec(query);
if(result && result[1])
return result[1].trim();
return '';
}
var lastUpper = false;
function strMatcher(id, strs)
{
return function findMatches(q, sync, async)
{
var pos = $(id).caret('pos');
q = (0 < pos) ? extractor(q.substring(0, pos)) : '';
if (q.length <= trigger.length)
return;
if (trigger.length)
{
if(trigger != q.substr(0, trigger.length))
return;
q = q.substr(trigger.length);
}
if (!q.match(validChars))
return;
var firstChar = q.substr(0, 1);
lastUpper = (firstChar === firstChar.toUpperCase() && firstChar !== firstChar.toLowerCase());
var cpos = $(id).caret('position');
$(id).parent().find('.tt-menu').css('left', cpos.left + 'px');
if (vertAdjustMenu)
$(id).parent().find('.tt-menu').css('top', (cpos.top + cpos.height) + 'px');
var matches = [];
var matches = [], substrRegex = new RegExp(q, 'i');
$.each(strs, function(i, str)
{
if (str.length > q.length && substrRegex.test(str))
matches.push(str);
});
if (!matches.length)
return;
sync(matches);
};
};
var lastVal = '';
var lastPos = 0;
function beforeReplace(event, data)
{
lastVal = $(id).val();
lastPos = $(id).caret('pos');
return true;
}
function onReplace(event, data)
{
if (!data || !data.length)
return;
if (!lastVal.length)
return;
var root = lastVal.substr(0, lastPos);
var post = lastVal.substr(lastPos);
var typed = extractor(root);
if (!lastUpper && typed.length >= root.length && 0 >= post.length)
return;
var str = root.substr(0, root.length - typed.length);
str += lastUpper ? (data.substr(0, 1).toUpperCase() + data.substr(1)) : data;
var cursorPos = str.length;
str += post;
$(id).val(str);
$(id).caret('pos', cursorPos);
}
this.typeahead =
$(id).typeahead({hint: false, highlight: false}, {'limit': 5, 'source': strMatcher(id, data)})
.on('typeahead:beforeselect', beforeReplace)
.on('typeahead:beforeautocomplete', beforeReplace)
.on('typeahead:beforecursorchange', beforeReplace)
.on('typeahead:selected', function(event,data){setTimeout(function(){ onReplace(event, data); }, 0);})
.on('typeahead:autocompleted', onReplace)
.on('typeahead:cursorchange', onReplace)
;
}
EDIT: Realizing the previous code had too much extra stuff, I narrowed it down to a minimal working example.
This is what was previously posted..
Solution 4
I'm late to the party, but this is what I came up with for Bootstrap v4. It also requires d3 (as I am not so familiar with jQuery).
You can see an example running at http://sumneuron.gitlab.io/multitags/
It has some useful features, is written in a clearly, and should get anyone who finds this off to a good start. Especially if you were ever curious as to how tagging is implemented.
Code is available at https://gitlab.com/SumNeuron/multitags/pipelines
The logic is roughly as follows:
if keydown in [enter, ","]:
// logic of function "doneTyping"
text = parse(text) // get text from textarea and grab latest value
if text is valid:
renderTag(text) // put the text in a tag element
updateHiddenForm(text) // update the hidden form to include the tag
else:
notifyUserOfInvalidTag(text) // alert user
else:
// logic of function "stillTyping"
suggest = bloodhoundSearch(text) // use twitter typeahead
updateSuggestionBox(suggest) // display results from typeahead
Solution 5
I guess you could edit the plugin to allow multiple selections (just don't close the drop-down menu) and add the selected values separated by comma. The only problem I see is that you don't know when to close the drop-down.
Related videos on Youtube
denislexic
Started learning programming on youtube and forums. Now I am addicted to it.
Updated on July 09, 2022Comments
-
denislexic almost 2 years
I'm using Twitter Bootstrap with Jquery. I want to use the TYPEAHEAD function for a textarea, which I got to work super easily. But I also need it to allow multiple selection.
By that I mean, after I selected one word from the autocomplete, it takes me back to textarea with an extra space afterwards and then if I start typing again, it offers me to do it again.
Here is a JS bin: http://jsbin.com/ewubuk/1/edit (Nothing special inside).
Is there an easy solution to allow multiple selection with typeahead? If yes, how?
Thanks in advance.
-
Glorious Kale about 11 yearsHmm this seemed like a great plan, but it doesn't work for bootstrap 2.3.1 anymore. At least for me. updater method doesn't do anything for me, but the typehead works as single.
-
Glorious Kale about 11 yearsOk, I missed the HTML part. I put <input type="text" data-provide="typeahead" /> instead of <input type="text" class="typeahead" />
-
Sherbrow about 11 years@IvanIvković If you encounter problems, you should try in something like jsfiddle and ask a new question about it (referencing this question). But I see you solved it, so that's great !
-
Glorious Kale about 11 yearsHmm strange I still can't answer any questions even dogh I've contributed. :/ Thanks. :)
-
andrewtweber about 11 yearsI am experiencing a problem with this in both the jsfiddle and my own code. It only seems to work if you start typing in lowercase. If you type a capital letter first, nothing is returned.
-
andrewtweber about 11 yearsOkay I think the solution was in matcher,
return ~item.toLowerCase().indexOf(tquery.toLowerCase())
-
Sherbrow over 10 years@AtaS. This plugin has been removed from twbs. Check the version you are using, and if you are using typeahead.js you should look for doc/questions related to that only.
-
rilar about 10 yearsAny idea how to do this with Strap Angular typeahead? mgcrea.github.io/angular-strap/##typeaheads
-
lucentx over 9 yearsIs there a way to make this work like you can select multiple items by doing ctrl + click?
-
andrewtweber over 9 yearsJust a note, the authors of typeahead have stated that they never plan to add this feature. They actually recommend using Select2 in this case as well. github.com/twitter/typeahead.js/issues/135
-
Sherbrow about 6 years@SumNeuron that part of the library was already on its way to disappear, if you want the same with newer versions of the library, you might have to use another one or reimplement it yourself
-
SumNeuron about 6 years@Sherbrow ok, I just wanted to confirm prior to putting effort in. Won't be too hard, but why reinvent the wheel
-
SumNeuron about 6 years+1 for updating old code, although it is easy enough to do. I know why you are using regex but it does obfuscate editing your code for others. The only other things - that in my eyes would improve your answer - is new lines instead of same line and not using @.
-
SumNeuron about 6 years@Sherbrow it would be cool - time permitting - if you could take a look and give some feedback stackoverflow.com/a/49601565/5623899