Modify jQuery autocomplete not to submit eagerly on Enter

后端 未结 4 953
悲哀的现实
悲哀的现实 2021-01-14 22:55

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

相关标签:
4条回答
  • 2021-01-14 23:31

    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:

    • To change all the autocomplete widgets you need to use jQuery's prototype, $.fn is an alias to $.prototype.
    • Also you need to change $.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)
    • You might say, Hey you keep register the very same callback for the 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
    • Adding code to a javascript function programmatically
    0 讨论(0)
  • 2021-01-14 23:36

    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>

    0 讨论(0)
  • 2021-01-14 23:49

    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

    0 讨论(0)
  • 2021-01-14 23:52

    I was somehow against overriding the jqueryui implementation and did the following:

    • in the close event of the autocomplete i set a flag "doNotSubmit" when enter was pressed
    • in your case then i'd bound a submit event listener to the form which checks the doNotSubmit flag and acts accordingly.

    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.

    0 讨论(0)
提交回复
热议问题