Thursday, January 1, 2009

Handling textbox onchange event in javascript

How to detect a change in textbox (or html input) on the clientside (javascript)? This is a common problem when we want to do something as soon as textbox (input box) change "event" is fired.

Different ways textboxes (input fields) can be changed

1) Textbox change event fired by keyboard,

2) Textbox change event raised because of copying into textbox via ctrl-c and ctrl-v (and mac equivalent)

3) Textbox change event raised by copying into textbox via context menu (right mouse click and then left mouse click)

4) Textbox change event fired by Autofill applications - this is often overlooked and difficult to control because the user could be using a variety of form filling applications out there including roboform, google's autofill, yahoo's autofill and so on.

5) Textbox change triggered by the history stored by the browser - This is browser specific (firefox, safari) but still tricky as user hasn't typed anything, he/she has just moved focus to the textbox (via mouse click, tabs etc) and selected the values present in history by either mouse click or pressing enter key!

Specific example: We have a textbox (input field) for zipcodes and as user finishes typing the zipcode we want to update city and state fields to show the city/state for the entered zipcode. This is useful for doing a dynamic real time validation as well so that we can tell the user that his zipcode is invalid as soon as he enters it.


Possible solutions

1) Textbox onblur event - This is very good and works most of the time. But it'll be fired even if you move your mouse over the textbox so if your textbox change event handling mechanism involves any graphical changes (making things visible/invisible etc) then it might be disruptive.

2) Textbox mouseout event - Works but not a complete solution as you need to handle keyboard and copy paste events too. Further, mouseout event is triggered when user clicks somewhere outside the textbox and not when he finishes typing! So, if your text change handler does things like updating another controls etc based on the new text in the textbox, then it won't work.

3) Textbox onchange event - By far the best if combined with some additional code to disable copying/pasting something in the textbox. The copying/pasting can be easily handled in windows by detecting and invalidating control key press. One example of the code would be -

function OnTextChange(obj, e)
{
var isCtrl = false;
if(window.event) {
key = e.keyCode;
isCtrl = window.event.ctrlKey
}
else if(e.which) {
key = e.which;
isCtrl = e.ctrlKey;
}
if(isCtrl)
return false;
}

Code presented above is not optimal and works only in windows but just to give some idea. Further, we still have the Autofill or Form filling applications or selecting the value from history (firefox) that can still change the textbox without triggering any event.

4) Some browser specific solutions - onpaste attribute for input boxes (textbox) in firefox and I believe some safari specific attributes are also present. Some autofill variety of attributes are also present that can stop the autofilling for the whole form.

5) In the context of ASP.net, one solution is to put the textbox in an updatepanel and catch the textbox's server side change event! But in my view UpdatePanels can get very slow in a variety of cases and severely disrupt the user experience and are recommended only if the scope is limited, user responsiveness is not a huge problem or you have a reason to believe that the they will perform consistently across all browsers and operating systems.

Most solutions work as a combinations of above and by limiting the ways the user can actually enter the data, might solve the problem of handling the onchange event. I personally think that limiting the ways user can enter the data again disrupts the experience by violating the principle of least surprise! A user who is used to copying/pasting the SSN, zipcode or address (or any difficult to type/remember field) might hit back if ctrl-v doesn't do anything ("form is broken"). Another user who relies on autofill / form-fillers a lot might again abandon the form midway. Allowing the user to freedom to use whatever tools he wants to use to complete the form will obviously result in better results.


Final Solution for detecting any type of change in textbox

So, as you can see the core issue is that there is no textbox change event built in the browsers! This is logically so too because browser has no way of knowing when the user has finished changing the textbox other then some out of focus or mouse out events but they don't help us if we want to handle the textbox change immediately after use has finished it.

This is more of a hack and not a very neat / clean solution but the good news is that it takes care of *all* scenarios in which the user can change a textbox! Keyboard, mouse, pasting, selecting from history, autofill - they will all be handled perfectly without compromising user's experience.

The basic premise is to check the textbox for any changes every 1-2 seconds and if any change is detected, trigger the onchange event handler. Sample code is below -

1. Include this code in your scripts section -

<script language="javascript" type="text/javascript">
checkLastTextBoxValue($("[id$=textBox]").val());
</script>

This will basically run the checkLastTextBoxValue function as soon as the page is loaded. "textBox" is the id of your textbox (or input field) control. I've added the $ sign to take care of the ASP.net's textbox control's client id issue.

2. A sample function definition would be as follows -

function checkLastTextBoxValue (oldValue)
{
if($("[id$=textBox]").val() != oldValue)
{
if(String($("[id$=textBox]").val()).length == 5) //This is for USA zips
{
zip = $("[id$=textBox]").val();
textBoxOnChangeEventHandler(zip);
}
}
setTimeout("checkLastTextBoxValue('" + $("[id$=textBox]").val() + "')", 1000);
}


This code has three things to be noted -

a. if($("[id$=textBox]").val() != oldValue) - we compare "old value" with the "current value" in the textbox to simulate "onchange" event for the textbox! Not elegant but crafty and wickedly accurate!

b. textBoxOnChangeEventHandler(zip); - we do what needs to be done after we have "detected" a change in the textbox here.

c. setTimeout("checkLastTextBoxValue('" + $("[id$=textBox]").val() + "')", 1000); - pretty much the core of this textbox change event handling mechanism, this basically sets up the checkLastTextBoxValue function to run after a second. This doesn't eat up or block CPU though as we are taking 1000 milliseconds worth of break (and it can be more depending on our requirement) between the calls.

So after much research and caffeine, I figured that this is the most robust and user-responsiveness-friendly solution for detecting textbox or input field changes.

Feel free to comment.

2 comments:

  1. really good articles...thanks a lot...

    ReplyDelete
  2. Good Article, however can we have something else instead of capturing th value every 1000 millisecond..

    ReplyDelete