Modifier l’auto-complétion jQuery pour ne pas soumettre avec impatience le bouton Entrée

Nos utilisateurs se plaignent de ce que, lorsqu’ils appuient sur la touche Entrée après avoir collé ou saisi des valeurs dans un widget jQuery autocomplete, le formulaire est soumis.

Cela les énerve énormément quand ils copient-collent une valeur existante dans les options de saisie semi-automatique, le widget de saisie semi-automatique s’ouvre pour afficher cette valeur unique. Ils appuient sur Entrée pour accepter cette valeur. Le formulaire est ensuite soumis avant d’avoir par défaut et nous ne voulons pas le changer), le widget ne sélectionnera pas la première option du menu.

Type C and press Enter:
$('form').submit(function () { alert('You submitted the form'); return false; }); $('#autocomplete').autocomplete({ source: ["c#", "c", "c++", "java", "php", "coldfusion"] });

DEMO du problème

Comment pouvons-nous changer le fait que cliquer sur Entrée ne ferme que les suggestions de saisie semi-automatique?

Il semble que jQuery UI n’ait pas laissé de porte dérobée pour personnaliser le widget, vous pouvez donc remplacer la fonction de autocompleteautocomplete pour enregistrer un rappel pour l’événement onkeypress , capturer l’ Enter et arrêter la propagation afin qu’elle soit onkeypress ‘ Ne soumettez pas le formulaire si le widget est ouvert = visible.

Voici comment ça se passe:

 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


Remarques:

  • Pour modifier tous les widgets de saisie semi-automatique dont vous avez besoin pour utiliser le prototype de jQuery, $.fn est un alias en $.prototype .
  • Vous devez également modifier $.fn.autocomplete avant de l’utiliser, $.fn.autocomplete les modifications apscopes ne s’appliqueront pas à ces widgets.
  • this à l’intérieur de la fonction autocomplete est en fait un object jQuery, vous n’avez donc pas besoin de l’envelopper avec $(this)
  • Vous pourriez dire, hé, vous continuez à enregistrer le même rappel pour l’événement de keypress . Eh bien, c’est exactement ce que je fais et pourquoi j’ai écrit le rappel en tant que fonction nommée. Si vous passez le même rappel à addEventListener il ne l’enregistrera qu’une seule fois. MDN , Spécifications
  • Ajout de code à une fonction javascript par programme

J’aurais peut-être une solution plus simple en utilisant les événements autocompleteclose et autocompleteopen jQuery autocomplete .

Voir le code ci-dessous:

 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"] }); 
    
Type C and press Enter:

J’étais en quelque sorte contre le dépassement de l’implémentation de jqueryui et j’ai fait ce qui suit:

  • dans l’événement de clôture de l’auto-complétion, je mets un drapeau “doNotSubmit” quand on appuie sur enter
  • dans votre cas, j’aurais lié un écouteur d’événement à soumettre au formulaire qui vérifie l’indicateur doNotSubmit et agit en conséquence.

L’idée de base est que l’événement de fermeture de jqueryui est déclenché avant l’événement keyup ou submit et vous donne le code d’activation. Vous pouvez donc, à un autre endroit (keyup, submit, etc.) consumr une entrée non désirée ou une autre pression de touche.

Cette question est similaire à celle-ci . Bien que les solutions sur cette page soient simples, elles dépendent des identifiants des éléments sur la page. J’utilise autocomplete sur de nombreuses pages, je préfère donc l’approche de gdoron (sur cette page). Merci à lui d’avoir fait le gros du travail.

Cependant, je pense qu’il y a un bug dans son code. Si vous accédez à un champ de saisie semi-automatique qui contient déjà du contenu et tapez un retour, il soumettra le formulaire. Voici un correctif (le changement est le deuxième “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); }; })(); 

Démo en direct