Question

I have some textboxes on a page and I want to click a link when the user presses enter in any of them.

I can easily trap the enter button using javascript (by looking for 13 in event.keyCode and event.which), but I hit an issue when the browser's autocomplete feature kicks in and suggests what the user might want to type. We're finding the users often press enter to accept the browser's suggestion, rather than tab. This confuses the users as the link is clicked immediately, whereas they still intended to enter text into some of the other fields.

I know it would be better to use a form and a submit button here, but for various reasons that's not practical.

I'm using jQuery, so feel free to offer jQuery solutions.

Was it helpful?

Solution

User always have to press the down key if they choose to select one of the auto complete text right, why not set a variable to something when they press the down key, and then if they do the enter press afterwards you check the variable. You should not do the link click function if the variable is set, otherwise do it as normal.

OTHER TIPS

Using tuanvt's idea in the accepted answer, I wrote a jQuery plugin that does the job.

I track when the user presses the up, down, page-up and page-down keys to tell when they're in the autocomplete box. All other keys imply they've left it.

I ensure that we only apply these rules to textboxes: all other input elements behave normally.

Opera already does a pretty good job of what I was trying to achieve, so I don't enforce my rules in that browser - otherwise the user would have to press enter twice.

Tested in IE6, IE7, IE8, Firefox 3.5.5, Google Chrome 3.0, Safari 4.0.4, Opera 10.00.

It's available on jquery.com as the SafeEnter plugin. For your convenience, the code for release 1.0 is as follows:

// jQuery plugin: SafeEnter 1.0
// http://plugins.jquery.com/project/SafeEnter
// by teedyay
//
// Fires an event when the user presses Enter, but not whilst they're in the browser's autocomplete suggestions

//codesnippet:2e23681e-c3a9-46ce-be93-48cc3aba2c73
(function($)
{
    $.fn.listenForEnter = function()
    {
        return this.each(function()
        {
            $(this).focus(function()
            {
                $(this).data('safeEnter_InAutocomplete', false);
            });
            $(this).keypress(function(e)
            {
                var key = (e.keyCode ? e.keyCode : e.which);
                switch (key)
                {
                    case 13:
                        // Fire the event if:
                        //   - we're not currently in the browser's Autocomplete, or
                        //   - this isn't a textbox, or
                        //   - this is Opera (which provides its own protection)
                        if (!$(this).data('safeEnter_InAutocomplete') || !$(this).is('input[type=text]') || $.browser.opera)
                        {
                            $(this).trigger('pressedEnter', e);
                        }
                        $(this).data('safeEnter_InAutocomplete', false);
                        break;

                    case 40:
                    case 38:
                    case 34:
                    case 33:
                        // down=40,up=38,pgdn=34,pgup=33
                        $(this).data('safeEnter_InAutocomplete', true);
                        break;

                    default:
                        $(this).data('safeEnter_InAutocomplete', false);
                        break;
                }
            });
        });
    };

    $.fn.clickOnEnter = function(target)
    {
        return this.each(function()
        {
            $(this)
                .listenForEnter()
                .bind('pressedEnter', function()
                {
                    $(target).click();
                });
        });
    };
})(jQuery);

Try setting the autocomplete off on your check box, though this is not standard for all browser but it works on the common browsers.

<input type="text" autocomplete="off" />

JQuery is nice and all but why not use simple JavaScript?

function submit_on_enter(e) {
    var keycode;
    if (window.event) keycode = window.event.keyCode;
    else if (e) keycode = (e.keyCode ? e.keyCode : e.which);
    else return true;

    if (keycode == 13) {
        if (window.previousKeyCode) {
            // down=40,up=38,pgdn=34,pgup=33
            if (window.previousKeyCode == 33 || window.previousKeyCode == 34 ||
                window.previousKeyCode == 39 || window.previousKeyCode == 40) {
                    window.previousKeyCode = keycode;
                    return true;
            }
        }
        submit_form();
        return false;
    } else {
        window.previousKeyCode = keycode;
        return true;
    }
}

track down enter key on keyup event, selection is completed by the time you detect enter key on keyup event.

detecting enter key on keydown and do operation interfere with autocomplete functionality.

This works for me:

$('input').keypress(function(e) {
    if(e.which == 13) {
        if(this.value)
        {
            $("#submitbutton").click();
        }
    }
});

If you're using the new html5 form field types, you may wish to alter teedyay's code as follows:

case 13:
                    // Fire the event if:
                    //   - we're not currently in the browser's Autocomplete, or
                    //   - this isn't a textbox, or
                    //   - this is Opera (which provides its own protection)
                    if (!$(this).data('safeEnter_InAutocomplete') || !$(this).is('input([type=text],[type=email],[type=number],[type=search],[type=tel],[type=url])') || $.browser.opera)
                    {
                        ...

Note the new input types.

I'm not sure if you are trapping key presses on the window, or on specific DOM elements. You should do the latter (e.g. on the form element), then in your event handler, look into the event object to determine which DOM element was the origin of the event. If this was a text field with autocomplete, then return false;.

Take a look at jQuery's .keypress() event handler, and the event.target property of the event object.

I faced the same issue and while not perfect I used another approach which I consider simpler. When creating the autocomplete I set the current time for the select event and then compare it before taking any actions:

$('#completer').autocomplete({
  source: ['First', 'Last'], delay: 0, minLength: 0,
  select: function(ev, ui) {
    $(ev.target).data('lastAutocompleteSelectTimestamp', new Date().getTime())
  }
})

$(document).on('keydown', '#completer', function(ev){
  if (ev.which != 13) return
  var lastAutocompletionTime = $(ev.currentTarget).data('lastAutocompleteSelectTimestamp')
  if (!lastAutocompletionTime || (new Date().getTime() - lastAutocompletionTime) > 100)
    alert('Enter pressed outside autocomplete context')
})
<html>
<head>
  <link href="https://code.jquery.com/ui/1.11.0/themes/smoothness/jquery-ui.css" rel="stylesheet"/>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
  <script src="https://code.jquery.com/ui/1.11.0/jquery-ui.min.js"></script>
</head>
<body>
  <input id=completer type=text />
</body>
</html>

Another (safer) approach is to use the keyup and keydown events to detect value changes.

Autocomplete sets the value after the keyDown event. When both events are observed, the following is much more reliable than the other solutions:

var tempValue;
// fire when enter is 1. pressed and 2. released
function handlerEnter(type){
     // save the value for comparison
     if(type == 'keyDown'){
         tempValue = document.activeElement.value;
         return null; // quit
     }

     // quit when the value changed in between keyDown & keyUp
     if(type == 'keyUp' && tempValue != document.activeElement.value){
         return null;
     }

     // autocomplete wasn't used
     doStuff();
}

You might want to check if document.activeElement is an input. Feel free to use my evil prototype extension.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top