2
0
mirror of https://github.com/devbridge/jQuery-Autocomplete.git synced 2024-09-19 08:49:01 +00:00

Add ability to select suggestion if it matches typed value. Fixes #112.

This commit is contained in:
Tomas Kirda 2013-11-23 21:40:52 -06:00
parent 6e7bbf3ea5
commit 3542ada13f
6 changed files with 161 additions and 89 deletions

View File

@ -42,6 +42,7 @@
<script type="text/javascript" src="scripts/jquery-1.8.2.min.js"></script>
<script type="text/javascript" src="scripts/jquery.mockjax.js"></script>
<script type="text/javascript" src="src/jquery.autocomplete.js"></script>
<script type="text/javascript" src="scripts/countries.js"></script>
<script type="text/javascript" src="scripts/demo.js"></script>
</body>
</html>

View File

@ -33,6 +33,8 @@ The standard jquery.autocomplete.js file is around 2.7KB when minified via Closu
* `onSearchStart`: `function (query) {}` called before ajax request. `this` is bound to input element.
* `onSearchComplete`: `function (query) {}` called after ajax response is processed. `this` is bound to input element.
* `onSearchError`: `function (query, jqXHR, textStatus, errorThrown) {}` called if ajax request fails. `this` is bound to input element.
* `onInvalidateSelection`: `function () {}` called when input is altered after selection has been made. `this` is bound to input element.
* `triggerSelectOnValidInput`: Boolean value indicating if `select` should be triggered if it matches suggestion. Default `true`.
* `beforeRender`: `function (container) {}` called before displaying the suggestions. You may manipulate suggestions DOM before it is displayed.
* `tabDisabled`: Default `false`. Set to true to leave the cursor in the input field after the user tabs to select a suggestion.
* `paramName`: Default `query`. The name of the request parameter that contains the query.

View File

@ -1,4 +1,4 @@
{
var countries = {
"AD": "Andorra",
"AE": "United Arab Emirates",
"AF": "Afghanistan",

View File

@ -1,78 +1,68 @@
/*jslint browser: true, white: true, plusplus: true */
/*global $: true */
/*global $, countries */
$(function () {
'use strict';
// Load countries then initialize plugin:
$.ajax({
url: 'content/countries.txt',
dataType: 'json'
}).done(function (source) {
var countriesArray = $.map(countries, function (value, key) { return { value: value, data: key }; });
var countriesArray = $.map(source, function (value, key) { return { value: value, data: key }; }),
countries = $.map(source, function (value) { return value; });
// Setup jQuery ajax mock:
$.mockjax({
url: '*',
responseTime: 2000,
response: function (settings) {
var query = settings.data.query,
queryLowerCase = query.toLowerCase(),
re = new RegExp('\\b' + $.Autocomplete.utils.escapeRegExChars(queryLowerCase), 'gi'),
suggestions = $.grep(countriesArray, function (country) {
// return country.value.toLowerCase().indexOf(queryLowerCase) === 0;
return re.test(country.value);
}),
response = {
query: query,
suggestions: suggestions
};
// Setup jQuery ajax mock:
$.mockjax({
url: '*',
responseTime: 2000,
response: function (settings) {
var query = settings.data.query,
queryLowerCase = query.toLowerCase(),
re = new RegExp('\\b' + $.Autocomplete.utils.escapeRegExChars(queryLowerCase), 'gi'),
suggestions = $.grep(countriesArray, function (country) {
// return country.value.toLowerCase().indexOf(queryLowerCase) === 0;
return re.test(country.value);
}),
response = {
query: query,
suggestions: suggestions
};
this.responseText = JSON.stringify(response);
}
});
// Initialize ajax autocomplete:
$('#autocomplete-ajax').autocomplete({
// serviceUrl: '/autosuggest/service/url',
lookup: countriesArray,
lookupFilter: function(suggestion, originalQuery, queryLowerCase) {
var re = new RegExp('\\b' + $.Autocomplete.utils.escapeRegExChars(queryLowerCase), 'gi');
return re.test(suggestion.value);
},
onSelect: function(suggestion) {
$('#selction-ajax').html('You selected: ' + suggestion.value + ', ' + suggestion.data);
},
onHint: function (hint) {
$('#autocomplete-ajax-x').val(hint);
},
onInvalidateSelection: function() {
$('#selction-ajax').html('You selected: none');
}
});
// Initialize autocomplete with local lookup:
$('#autocomplete').autocomplete({
lookup: countriesArray,
minChars: 0,
onSelect: function (suggestion) {
$('#selection').html('You selected: ' + suggestion.value + ', ' + suggestion.data);
}
});
// Initialize autocomplete with custom appendTo:
$('#autocomplete-custom-append').autocomplete({
lookup: countriesArray,
appendTo: '#suggestions-container'
});
// Initialize autocomplete with custom appendTo:
$('#autocomplete-dynamic').autocomplete({
lookup: countriesArray
});
this.responseText = JSON.stringify(response);
}
});
// Initialize ajax autocomplete:
$('#autocomplete-ajax').autocomplete({
// serviceUrl: '/autosuggest/service/url',
lookup: countriesArray,
lookupFilter: function(suggestion, originalQuery, queryLowerCase) {
var re = new RegExp('\\b' + $.Autocomplete.utils.escapeRegExChars(queryLowerCase), 'gi');
return re.test(suggestion.value);
},
onSelect: function(suggestion) {
$('#selction-ajax').html('You selected: ' + suggestion.value + ', ' + suggestion.data);
},
onHint: function (hint) {
$('#autocomplete-ajax-x').val(hint);
},
onInvalidateSelection: function() {
$('#selction-ajax').html('You selected: none');
}
});
// Initialize autocomplete with local lookup:
$('#autocomplete').autocomplete({
lookup: countriesArray,
minChars: 0,
onSelect: function (suggestion) {
$('#selection').html('You selected: ' + suggestion.value + ', ' + suggestion.data);
}
});
// Initialize autocomplete with custom appendTo:
$('#autocomplete-custom-append').autocomplete({
lookup: countriesArray,
appendTo: '#suggestions-container'
});
// Initialize autocomplete with custom appendTo:
$('#autocomplete-dynamic').autocomplete({
lookup: countriesArray
});
});

View File

@ -66,14 +66,15 @@ describe('Autocomplete', function () {
context,
value,
data,
autocomplete = new $.Autocomplete(input, {
autocomplete = $(input).autocomplete({
lookup: [{ value: 'A', data: 'B' }],
triggerSelectOnValidInput: false,
onSelect: function (suggestion) {
context = this;
value = suggestion.value;
data = suggestion.data;
}
});
}).autocomplete();
input.value = 'A';
autocomplete.onValueChange();
@ -260,7 +261,7 @@ describe('Autocomplete', function () {
ajaxExecuted = true;
var response = {
query: null,
suggestions: ['A', 'B', 'C']
suggestions: ['Aa', 'Bb', 'Cc']
};
this.responseText = JSON.stringify(response);
}
@ -276,7 +277,7 @@ describe('Autocomplete', function () {
runs(function () {
expect(ajaxExecuted).toBe(true);
expect(autocomplete.suggestions.length).toBe(3);
expect(autocomplete.suggestions[0].value).toBe('A');
expect(autocomplete.suggestions[0].value).toBe('Aa');
});
});
@ -496,4 +497,44 @@ describe('Autocomplete', function () {
expect(context).toBe(element);
expect(elementCount).toBe(1);
});
it('Should trigger select when input value matches suggestion', function () {
var input = $('<input />'),
instance,
suggestionData = false;
input.autocomplete({
lookup: [{ value: 'Jamaica', data: 'J' }],
triggerSelectOnValidInput: true,
onSelect: function (suggestion) {
suggestionData = suggestion.data;
}
});
input.val('Jamaica');
instance = input.autocomplete();
instance.onValueChange();
expect(suggestionData).toBe('J');
});
it('Should NOT trigger select when input value matches suggestion', function () {
var input = $('<input />'),
instance,
suggestionData = null;
input.autocomplete({
lookup: [{ value: 'Jamaica', data: 'J' }],
triggerSelectOnValidInput: false,
onSelect: function (suggestion) {
suggestionData = suggestion.data;
}
});
input.val('Jamaica');
instance = input.autocomplete();
instance.onValueChange();
expect(suggestionData).toBeNull();
});
});

View File

@ -75,6 +75,7 @@
tabDisabled: false,
dataType: 'text',
currentRequest: null,
triggerSelectOnValidInput: true,
lookupFilter: function (suggestion, originalQuery, queryLowerCase) {
return suggestion.value.toLowerCase().indexOf(queryLowerCase) !== -1;
},
@ -390,32 +391,57 @@
onValueChange: function () {
var that = this,
q;
options = that.options,
value = that.el.val(),
query = that.getQuery(value),
index;
if (that.selection) {
that.selection = null;
(that.options.onInvalidateSelection || $.noop)();
(options.onInvalidateSelection || $.noop).call(that.element);
}
clearInterval(that.onChangeInterval);
that.currentValue = that.el.val();
q = that.getQuery(that.currentValue);
that.currentValue = value;
that.selectedIndex = -1;
if (q.length < that.options.minChars) {
// Check existing suggestion for the match before proceeding:
if (options.triggerSelectOnValidInput) {
index = that.findSuggestionIndex(query);
if (index !== -1) {
that.select(index);
return;
}
}
if (query.length < options.minChars) {
that.hide();
} else {
that.getSuggestions(q);
that.getSuggestions(query);
}
},
findSuggestionIndex: function (query) {
var that = this,
index = -1,
queryLowerCase = query.toLowerCase();
$.each(that.suggestions, function (i, suggestion) {
if (suggestion.value.toLowerCase() === queryLowerCase) {
index = i;
return false;
}
});
return index;
},
getQuery: function (value) {
var delimiter = this.options.delimiter,
parts;
if (!delimiter) {
return $.trim(value);
return value;
}
parts = value.split(delimiter);
return $.trim(parts[parts.length - 1]);
@ -498,15 +524,25 @@
}
var that = this,
formatResult = that.options.formatResult,
options = that.options,
formatResult = options.formatResult,
value = that.getQuery(that.currentValue),
className = that.classes.suggestion,
classSelected = that.classes.selected,
container = $(that.suggestionsContainer),
beforeRender = that.options.beforeRender,
beforeRender = options.beforeRender,
html = '',
index,
width;
if (options.triggerSelectOnValidInput) {
index = that.findSuggestionIndex(value);
if (index !== -1) {
that.select(index);
return;
}
}
// Build suggestions inner HTML:
$.each(that.suggestions, function (i, suggestion) {
html += '<div class="' + className + '" data-index="' + i + '">' + formatResult(suggestion, value) + '</div>';
@ -516,7 +552,7 @@
// because if instance was created before input had width, it will be zero.
// Also it adjusts if input width has changed.
// -2px to account for suggestions border.
if (that.options.width === 'auto') {
if (options.width === 'auto') {
width = that.el.outerWidth() - 2;
container.width(width > 0 ? width : 300);
}
@ -524,7 +560,7 @@
container.html(html);
// Select first value by default:
if (that.options.autoSelectFirst) {
if (options.autoSelectFirst) {
that.selectedIndex = 0;
container.children().first().addClass(classSelected);
}
@ -598,11 +634,13 @@
}
}
// Display suggestions only if returned query matches current value:
if (originalQuery === that.getQuery(that.currentValue)) {
that.suggestions = result.suggestions;
that.suggest();
// Return if originalQuery is not matching current query:
if (originalQuery !== that.getQuery(that.currentValue)) {
return;
}
that.suggestions = result.suggestions;
that.suggest();
},
activate: function (index) {