mirror of
https://github.com/devbridge/jQuery-Autocomplete.git
synced 2024-12-23 03:18:55 +00:00
Add ability to select suggestion if it matches typed value. Fixes #112.
This commit is contained in:
parent
6e7bbf3ea5
commit
3542ada13f
@ -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>
|
||||
|
@ -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.
|
||||
|
@ -1,4 +1,4 @@
|
||||
{
|
||||
var countries = {
|
||||
"AD": "Andorra",
|
||||
"AE": "United Arab Emirates",
|
||||
"AF": "Afghanistan",
|
126
scripts/demo.js
126
scripts/demo.js
@ -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
|
||||
};
|
||||
|
||||
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
|
||||
});
|
||||
// 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
|
||||
});
|
||||
});
|
@ -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();
|
||||
});
|
||||
});
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user