I recently come across an interesting request which read like this:
A simple webapplication with one Textbox and one label. The Textbox has Autopostback=”true” and has an event listener attached to the TextChanged event. In the TextChanged event the label’s text is set to the text in the textbox. A javascript function “formats” the value of the textbox on the keyUp event. The ”problem” The postback is not fired for a textbox if a javascript function sets the value of the textbox in the keyDown or keyUp event. This only occurs in IE (IE 8, IE 7, FF 3.6.8, Opera 10.6 and Chrome 5 has been tested). No javascript errors or any kind of error message, the postback is just not being fired.
And here’s a sample page to reproduce the problem:
<%@ Page Language="vb" AutoEventWireup="false" ValidateRequest="false" EnableEventValidation="false" EnableViewStateMac="false" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script type="text/javascript" language="javascript">
function FormatTextBox(obj) {
//This is just a dummy function that adds $ as the first character.
var value = obj.value
if (value.length > 0 && value.charAt(0) != '$')
value = '$' + value;
obj.value = value; // This line causes the autopostback not to fire. This only happens if the value of the input
// is set in the keyup or keydown event and only in IE.
}
</script>
<script language="vbscript" runat="server">
'Just to have something happening during the autopostback.
Private Sub TextBox1_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox1.TextChanged
Label1.Text = TextBox1.Text
End Sub
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:TextBox ID="TextBox1" runat="server" onkeyup="return FormatTextBox(this);" AutoPostBack="true"></asp:TextBox>
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
</div>
</form>
</body>
</html>
Type something in the textbox then hit TAB (or click somewhere on the white page) and you’ll see nothing happens, while you should have a postback and the Label should reflect the text you typed (plus the “$” sign added by the FormatTextBox function). Interestingly enough if you type only one character in the TextBox you’ll have the problem, if you type in two or more characters then everything will work. Another interesting effect: if you type the dollar sign (“$”) which happens to be the sign added by javascript, you’ll never have an automatic postback no matter how many “$” you’ll type in.
Why is it happening? Well, any time you type a new character into the TextBox, IE compares the new value to some internal ‘initial’ value that it it keeping to see if the text has changed. However, when you set the value of the TextBox programmatically, IE makes the assumption that you know what just happened and that it doesn’t have to fire the onChange event. So it resets its internal tracking value to be the same as the value you just programmatically set. Then when you tab out of focus, the two values are the same and no onChange event gets fired.
So, how to deal with it? The easiest solution is to use the OnBlur event or,
if you really need to hook on the onkeyup event, is to track the state of the TextBox on your own and force the onchange event to fire, even if you programatically set the TextBox value. Use a TextBox like this:
<asp:TextBox ID="TextBox1" runat="server" onkeyup="FormatTextBoxDirty(this)"
onblur="if (this.dirty){this.onchange();}" AutoPostBack="true" />
and javascript like this:
function FormatTextBoxDirty(obj) {
obj.dirty = false; // This prevents firing onchange twice in the event that we don't modify the value
if (obj.value.length > 0 && obj.value.charAt(0) != '$') {
obj.value = '$' + obj.value;
obj.dirty = true;
}
}
Here’s the working page:
<%@ Page Language="vb" AutoEventWireup="false" ValidateRequest="false" EnableEventValidation="false" EnableViewStateMac="false" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="Head1" runat="server">
<title></title>
<script type="text/javascript" language="javascript">
function FormatTextBox(obj) {
//This is just a dummy function that adds $ as the first character.
var value = obj.value
if (value.length > 0 && value.charAt(0) != '$')
value = '$' + value;
obj.value = value; // This line causes the autopostback not to fire. This only happens if the value of the input
// is set in the keyup or keydown event and only in IE.
}
function FormatTextBoxDirty(obj) {
obj.dirty = false; // This prevents firing onchange twice in the event that we don't modify the value
if (obj.value.length > 0 && obj.value.charAt(0) != '$') {
obj.value = '$' + obj.value;
obj.dirty = true;
}
}
</script>
<script language="vbscript" runat="server">
'Just to have something happening during the autopostback.
Private Sub TextBox1_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox1.TextChanged
Label1.Text = TextBox1.Text
End Sub
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:TextBox ID="TextBox1" runat="server" onkeyup="FormatTextBoxDirty(this)" onblur="if (this.dirty){this.onchange();}"
AutoPostBack="true" />
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
</div>
</form>
</body>
</html>
P.s.
Thanks to Steve Molloy for this help on this matter.
Carlo
Quote of the day:
Minds are like parachutes: they only work when they are open – Anonymous