Our users complain that when they press the enter key after pasting or typing values in a jQuery autocomplete widget the form is submitted.
It\'s extremely annoyin
It seems like jQuery UI didn't left a backdoor to customize the widget out of the box, so what you can do is override the autocomplete
function to register a callback for the onkeypress
event, capture the Enter
and stop the propagation so it won't submit the form if the widget is open=visible.
Here how it goes:
function cancelAutocompleteSumbission(e) {
// Make sure this is a nodeElement and the button pressed was Enter-Return
if (!this.nodeType || e.which != 13)
return;
// If the widget is visible we simply want to close the widget.
if ($(this).autocomplete('widget').is(':visible')) {
$(this).autocomplete('close');
return false;
}
}
// Making a private scope to avoid naming collision.
$.fn.autocomplete = (function () {
// Cache the old autocomplete function.
var oldAutocomplete = $.fn.autocomplete;
// This will be the new autocomplete function.
return function () {
// If the first argument isn't "destroy" which
// should restore the input to it's initial state.
if (!/^destroy$/i.test(arguments[0]))
// Attach event to the input which will prevent Enter submission as
// explained above.
this.keypress(cancelAutocompleteSumbission);
// We need to restore the input to it's initial state,
// detach the keypress callback.
else
this.off('keypress', cancelAutocompleteSumbission);
// Call the cached function with the give "this" scope and paramteres.
return oldAutocomplete.apply(this, arguments);
};
})();
Live DEMO
Notes:
$.fn.autocomplete
before you use it it or the changes you made won't apply to those widget.this
inside the autocomplete
function is actually a jQuery object so you don't need to wrap it with $(this)
keypress
event. Well, that's exactly what I'm doing and why I wrote the callback as a named function. If you pass the same callback to addEventListener
it will register it only once. MDN, Specifications I might have a simpler solution by using jQuery autocomplete's autocompleteclose
and autocompleteopen
events.
See the code below:
var flag = 0; //global variable
$("#autocomplete").on({
autocompleteclose: function (event, ui) {
flag = 1;
//set flag back to 0 with a short delay so the next keypress can submit form
setTimeout(function () {
flag = 0;
}, 100);
},
//if the autocomplete widget is open, don't submit form on keypress
autocompleteopen: function (event, ui) {
flag = 1;
}
});
$('body').keypress(function(e) {
if (e.keyCode == '13') {
if (flag != 0) {
e.preventDefault();
} else {
//submit form
}
}
});
var flag = 0; //global variable
$("#autocomplete").on({
autocompleteclose: function (event, ui) {
flag = 1;
//set flag back to 0 with a short delay so the next keypress can submit form
setTimeout(function () {
flag = 0;
}, 100);
},
//if the autocomplete widget is open, don't submit form on keypress
autocompleteopen: function (event, ui) {
flag = 1;
}
});
$('body').keypress(function (e) {
if (e.keyCode == '13') {
if (flag != 0) {
e.preventDefault();
} else {
//submit form
}
}
});
$('form').submit(function () {
alert('You submitted the form');
return false;
});
$('#autocomplete').autocomplete({
source: ["c#", "c", "c++", "java", "php", "coldfusion", "javascript", "asp", "ruby"]
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js"></script>
<script src="//code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
<link rel="stylesheet" type="text/css" href="//code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css">
<form>Type C and press Enter:
<input id="autocomplete" />
<input type="submit" value="submit" />
</form>
This question is similar to this one. While the solutions on that page are straight forward, they depend on the ID's of elements on the page. I use autocomplete on lots of pages so I prefer gdoron's approach (on this page). Thanks to him for doing the heavy lifting.
However, I think there is a bug in his code. If you go to an autocomplete field that already has content and type a return, it will submit the form. Here is a fix (the change is the second "if block in cancelAutocompleteSumbission):
function cancelAutocompleteSumbission(e) {
// Make sure this is a nodeElement and the button pressed was Enter-Return
if (!this.nodeType || e.which != 13)
return;
if (!$(this).autocomplete('widget').is(':visible') && e.which === 13){
return false;
}
// If the widget is visible we simply want to close the widget.
if ($(this).autocomplete('widget').is(':visible')) {
$(this).autocomplete('close');
return false;
}
}
// Making a private scope to avoid naming collision.
$.fn.autocomplete = (function () {
// Cache the old autocomplete function.
var oldAutocomplete = $.fn.autocomplete;
// This will be the new autocomplete function.
return function () {
// If the first argument isn't "destroy" which
// should restore the input to it's initial state.
if (!/^destroy$/i.test(arguments[0]))
// Attach event to the input which will prevent Enter submission as
// explained above.
this.keypress(cancelAutocompleteSumbission);
// We need to restore the input to it's initial state,
// detach the keypress callback.
else
this.off('keypress', cancelAutocompleteSumbission);
// Call the cached function with the give "this" scope and paramteres.
return oldAutocomplete.apply(this, arguments);
};
})();
Live Demo
I was somehow against overriding the jqueryui implementation and did the following:
The basic idea behind it, is that jqueryui's close event is triggered before keyup or submit events and gives you the keycode. So you can in another place (keyup, submit, etc.) consume that one unwanted enter or other keypress.