views:

818

answers:

3

In my form I have a set of input boxes where a user can input a value. On change of one of these boxes, the form automatically gets submitted.

The problem now is however that a user stays in the last field, takes the mouse and presses the OK button (of another form) without leaving the textbox first. The change event doesn't get triggered and the old, incorrect values get passed to the next page.

I want to trigger the onchange event after a few miliseconds of inactive keyboard. Just like most autocomplete plugins do.
I think I could implement a timer that starts timing the moment you enter an input field and gets resetted everytime a keystroke is handled and then when it reaches zero the onchange event gets triggered.

I'm not up for re-inventing the wheel and was wondering if such a function is available somewhere.
Suggestions?

A: 

I don't know that such a solution would be considered "re-inventing" anything. As you said, it sounds to be nothing more than a simple setTimeout once the page loads. After about 3,000 milliseconds, it runs form.submit().

I would probably restart the count-down with each keystroke too, to give the user enough time to make their entry.

Jonathan Sampson
You were right, I did. I'm just a pit affraid of browser differences in the keypress event. So far no problems though.
borisCallens
A: 

Does it not work to do an onBlur, so both when the user moves to the next field or clicks something else, the value is saved?

Ólafur Waage
the problem occurs when the user clicks on the submit button that uses the current value to go to another page. There won't be time to first resubmit the previous form before submitting the form that goes to the next page.
borisCallens
+5  A: 

I had a similar problem and created a jQuery plugin currently in use in an internal application. It should trigger the change event after the user is done typing.

If you are not using jQuery, the code is still adaptable to anything else.

jQuery.fn.handleKeyboardChange = function(nDelay)
{
    // Utility function to test if a keyboard event should be ignored
    function shouldIgnore(event) 
    { 
     var mapIgnoredKeys = {
       9:true, // Tab
      16:true, 17:true, 18:true, // Shift, Alt, Ctrl
      37:true, 38:true, 39:true, 40:true, // Arrows 
      91:true, 92:true, 93:true // Windows keys
     };
     return mapIgnoredKeys[event.which];
    }

    // Utility function to fire OUR change event if the value was actually changed
    function fireChange($element)
    {
     if( $element.val() != jQuery.data($element[0], "valueLast") )
     {
      jQuery.data($element[0], "valueLast", $element.val())
      $element.trigger("change");
     }
    }

    // The currently running timeout,
    // will be accessed with closures
    var timeout = 0;

    // Utility function to cancel a previously set timeout
    function clearPreviousTimeout()
    {
     if( timeout )
     { 
      clearTimeout(timeout);
     }
    }

    return this
    .keydown(function(event)
    {
     if( shouldIgnore(event) ) return;
     // User pressed a key, stop the timeout for now
     clearPreviousTimeout();
     return null; 
    })
    .keyup(function(event)
    {
     if( shouldIgnore(event) ) return;
     // Start a timeout to fire our event after some time of inactivity
     // Eventually cancel a previously running timeout
     clearPreviousTimeout();
     var $self = $(this);
     timeout = setTimeout(function(){ fireChange($self) }, nDelay);
    })
    .change(function()
    {
     // Fire a change
     // Use our function instead of just firing the event
     // Because we want to check if value really changed since
     // our previous event.
     // This is for when the browser fires the change event
     // though we already fired the event because of the timeout
     fireChange($(this));
    })
    ;
}

Usage:

$("#my_input").handleKeyboardChange(300).change(function()
{
    // value has changed!
});
Vincent Robert
nice. You took some situations into account I didn't
borisCallens
I did change $element.trigger("Keyboard.change");to $element.trigger("change");Was much better then my naive implementation.
borisCallens
Oh sorry for that, it was a namespaced event for this specific application. You were right to change it and I will change it the code above.
Vincent Robert