Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>I mostly copied from the <a href="http://jqueryui.com/autocomplete/#multiple%20%27jQuery%20UI%20multi%22" rel="nofollow">jQuery UI multi-select example</a>, but made a few changes. The goal was to work <em>exactly</em> how you described and that this could handle any method of input: appending to the string, inserting into the string, and copying and pasting. </p> <p>The two keys to modify the multi example to meet your needs, where the creation of custom filters and adding to the source method. Originally I changed the search method, but source gave me more control on how to display the choices (implement min length and keep showing after the last term was trimmed).</p> <p>When the source method is executed, which seems to get fired during all manner of input types (typing, pasting, cutting), I split the inputs and check <em>each</em> input for validity. I check each because if somebody pasted text, then something in the middle might become invalid where it was valid before. Anything before the last term gets the exact filter applied while the last element gets the from start filter applied. The last term is also treated differently in that it is trimmed to the point the unmatched input occurs.</p> <p>After that I update the input value if any changes have occurred. I then display the response to the lastTerm, taking into account the minLength value, which even the original multi example forgot to do.</p> <p>I believe my solution is the best possible as it handles all methods of input and is simple in that it only adds to one function from the original example. The one downside is that there are some inefficiencies created to keep the solution simple, but these are so minor as to not cause any noticeable performance effects.</p> <p><em>additional ideas</em>: One other idea, would be change the split regex to /,?\s*/ so that the comma was optional. In my testing it was natural to type space after each response. Another would be to update the input value every time so the comma spacing is consistent.</p> <h2><a href="http://jsfiddle.net/SduyC/5" rel="nofollow">jsFiddle</a></h2> <pre><code>var availableTags = ['Bird', 'Song', 'Happy']; function split(val) { return val.split(/,\s*/); } // removes the last term from the array, and adds newValue if given function removeLastTerm(val, newValue) { var terms = split(val); terms.pop(); if (newValue) { terms.push(newValue); } terms.push(''); return terms.join(', ');; } // filter from start position from: // http://blog.miroslavpopovic.com/jqueryui-autocomplete-filter-words-starting-with-term function filterFromStart(array, term) { var matcher = new RegExp('^' + $.ui.autocomplete.escapeRegex(term), 'i'); return $.grep(array, function (value) { return matcher.test(value.label || value.value || value); }); } function filterExact(array, term) { var matcher = new RegExp('^' + $.ui.autocomplete.escapeRegex(term) + '$', 'i'); return $.grep(array, function (value) { return matcher.test(value.label || value.value || value); }); } $('#tags') // don't navigate away from the field on tab when selecting an item .bind('keydown', function (event) { if (event.keyCode === $.ui.keyCode.TAB &amp;&amp; $(this).data('ui-autocomplete').menu.active) { event.preventDefault(); } }) .autocomplete({ minLength: 0, delay: 0, source: function (request, response) { var terms = split(request.term), lastTrimmed = false, lastTerm, originalMaxIndex = terms.length - 1, filteredMaxIndex; if (originalMaxIndex &gt;= 0) { // remove any terms that don't match exactly for (var i = originalMaxIndex - 1; i &gt;= 0; i--) { if (filterExact(availableTags, terms[i]).length == 0) { terms.splice(i, 1); } } filteredMaxIndex = terms.length - 1; // trim the last term until it matches something or is emty lastTerm = terms[filteredMaxIndex]; while (lastTerm.length != 0 &amp;&amp; filterFromStart(availableTags, lastTerm).length == 0) { lastTerm = lastTerm.substr(0, lastTerm.length - 1); lastTrimmed = true; } if (lastTrimmed) { // add modified LastTerm or reduce terms array if (lastTerm.length == 0) { terms.splice(filteredMaxIndex--, 1); terms.push(''); } else terms[filteredMaxIndex] = lastTerm; } if (filteredMaxIndex &gt;= 0) { // only execute if we've removed something if (filteredMaxIndex &lt; originalMaxIndex || lastTrimmed) { this.element.val(terms.join(', ')); } } else { this.element.val(request.term); } if (this.options.minLength &lt;= lastTerm.length) { response(filterFromStart(availableTags, lastTerm)); } else { response([]); } } else { response(filterFromStart(availableTags, '')); } }, focus: function () { // prevent value inserted on focus return false; }, select: function (event, ui) { // add the selected value to the input. this.value = removeLastTerm(this.value, ui.item.value); return false; } }); </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload