2006.07.27 10:13 PM

Programmatic Value Changes and the Onchange Event

Reader Ambalal left the following comment on a prior post of mine regarding the changing of textbox values in their onchange event handlers:

I am facing one problem.
I have a textbox on a form and this textbox readonly. I am setting value of this textbox programmatically. Now I need to capture this event and wants to call a function in javascript for setting other values on the form; tried with the onChange and onTextChange but not able to call javascript function.
Please, give me some way to implement this.

Thanks in advance.

Regards
Ambalal

What Ambalal is describing is the fact that HTML input, select, and other data entry controls do not call their onchange event handlers when their values are changed programmatically. They only call their onchange event handlers following interactive (i.e., user-initiated) changes. This behavior is different than what typically happens in traditional Windows forms applications, where textbox, listbox, etc. controls usually raise their "onchange" (or whatever) event following any change to their values, whether done programmatically or interactively.

Let's look at an example. The following HTML contains a text input control with an onchange event handler and an anchor with which to trigger a programmatic change of the textbox's value. It also includes a div for visualizing this activity.

<html>
  <head>
    <script type="text/javascript">
    
      function onChange(textbox) {
        log("onChange (" + textbox.value + ")");
      }
      
      function programmaticChange() {
        log("programmaticChange");
        var textbox = document.getElementById("textbox");
        textbox.value += "*";
      }
      
      function log(text) {
        document.getElementById("log").innerHTML += "<br>" + text;
      }
      
    </script>
  </head>
  <body>
    <input type="text" id="textbox" onchange="onChange(this)" />
    <a href="#" onclick="programmaticChange(); return false">programmaticChange</a>    
    <hr />
    <div id="log"></div>
  </body>
</html>

If you change the value of the textbox by typing something into it and tabbing out, you'll see that its onchange event handler is called. However, if you click the anchor, you'll only see the textbox's value change, you won't see a subsequent call to the textbox's onchange event handler.

I don't know how this DHTML behavior became the norm, but it does make sense (to me, anyway) that onchange event handlers should only be triggered by interactive changes to control values (the different behavior of Windows forms applications often causes developers to jump through ugly hoops to avoid these handlers during programmatic changes, such as occurs during form initialization). After all, any programmatic changes made to a control's value must be explicitly coded by a page's developer, so if the developer wants those explicit programmatic changes to trigger the same onchange logic caused by a user's interactive value changes, all the developer needs to do is call the control's onchange function themselves.

Here's the same HTML as above, but with one line added to the bottom of the programmaticChange function that does just that.

<html>
  <head>
    <script type="text/javascript">
    
      function onChange(textbox) {
        log("onChange (" + textbox.value + ")");
      }
      
      function programmaticChange() {
        log("programmaticChange");
        var textbox = document.getElementById("textbox");
        textbox.value += "*";
        if (typeof(textbox.onchange) == "function") textbox.onchange();
      }
      
      function log(text) {
        document.getElementById("log").innerHTML += "<br>" + text;
      }
      
    </script>
  </head>
  <body>
    <input type="text" id="textbox" onchange="onChange(this)" />
    <a href="#" onclick="programmaticChange(); return false">programmaticChange</a>    
    <hr />
    <div id="log"></div>
  </body>
</html>

Now, if the control being changed (textbox) has an onchange event handler, and that handler is in fact a function (I've seen otherwise, so always check), then the handler function is called after the control's value is changed. Pretty simple.

(Note how, via the magic of closures, the contextual "this" reference passed to the control's event handler function continues to work correctly even when the function is called directly.)

However, this is a pretty simplistic example. And it differs in a fundamental way from the scenario described by Ambalal. That difference deserves some consideration, as I believe it calls for a different approach. In Ambalal's scenario, the target textbox control is read-only. This means that the issue is not really about getting programmatic value changes to act like interactive changes, because there are no interactive changes. What Ambalal is really trying to do is use the control's onchange event handler to centralize the execution of certain logic following each programmatic change to the control's value.

That's a subtle difference, which could easily be made to work using the approach described above (i.e., simply calling the control's onchange event handler function after every explicit control value change). However, I believe that that approach obscures the code's real purpose. (And seeing an onchange event handler on an input element with a readonly attribute is fairly confusing.) So, in this case, rather than hijack the interactive onchange event handler, I suggest using a single function to consolidate the necessary value change and post-change logic and then calling this function any time the control's value needs to be programmatically changed.

The following HTML illustrates this approach. I don't know what sorts of things cause Ambalal's read-only textbox to be updated, or where the new values come from, so this example simply provides another textbox with which to specify new values and uses an anchor to cause the new values to be written to the read-only textbox.

<html>
  <head>
    <script type="text/javascript">
    
      function programmaticChange() {
        log("programmaticChange");
        setTextboxValue(document.getElementById("newValue").value);
      }
      
      function setTextboxValue(newValue) {
        var textbox = document.getElementById("textbox");
        // validate the value, if necessary
        // format the value, if necessary
        if (textbox.value == newValue) return;
        log("setTextbox (" + newValue + ")");
        textbox.value = newValue;
        // perform other post-change activities, if necessary
      }
      
      function log(text) {
        document.getElementById("log").innerHTML += "<br>" + text;
      }
      
    </script>
  </head>
  <body>
    Read-only textbox: <input type="text" id="textbox" readonly="readonly" />
    <br />
    New value: <input type="text" id="newValue" />
    <a href="#" onclick="programmaticChange(); return false">programmaticChange</a>    
    <hr />
    <div id="log"></div>
  </body>
</html>

In this example, all of the logic relating to the updating of the textbox's value appears in (and in larger examples could be called from) a single function: setTextboxValue. The setTextboxValue function takes a new value to place in the textbox, get's a reference to the textbox control, verifies that the new value is different from the old value, and changes the textbox's value to the new value.

Organizing the code in this way brings with it a number of small, but important, advantages. It's clearer, as there's no confusing onchange event handler on the read-only textbox. It encapsulates the logic used to reference the textbox (in this case by Id), preventing this logic from being repeated in other places and making future changes to the control's Id or retrieval technique easier to implement. And, it reduces all changes to the read-only control's actual value property to a single point, making it possible to apply consistent validation and/or formatting to all values written to the read-only control.

Note also how the setTextboxValue function checks that the new value differs from the existing value in the read-only textbox before setting the control's value and executing any post-change activity. The earlier examples didn't do this, but could have, and in cases where it's important not to execute any post-change activity (again) unless there is actually a change, probably should have.

Hope this helps. Good luck, Ambalal.


Comments

Just add onblur="this.value=this.value"

It will set the internal value to the visible value. Works perfect and the control behaves like you would expect it. Much better if and very simpel ;) Fix the problem in the root, not trough complicated functions.....

Herman Kruisman | 2006.08.03 08:25 AM

Well, Herman, I'm all for simple, but I'm not sure how one would use the onblur event to synchronize activities intended to occur following the programmatic update of a control which may not even have the focus when changed, as depicted by the examples above. Perhaps you could elaborate? Thanks.

ewbi.develops | 2006.08.03 11:29 AM

Other value changes should be fired by the altering code, not the control since it is not changed by the control.

The only point where is goes wrong is when we enter data in the textbox and use the onchange event to alter the .value of the control. This is why:

User enters text in control and leaves control
Control checks internal_value (NOT .value) against entered data
Control finds difference and fires onchange
- onchange code alters data by setting .value
- .value property writes set value also to internal_value
- onchange code finished
Control stores entered data in internal_value directly

At this point the data on the screen is different from the internal_value. This is the bug. The controls stores the entered data directly in the internal_value. When we enter the input data again (not shown data) it is compared to the internal_value and matches so no onchange event is fired.

When we readout the .value it gives the shown data, not the internal value. To solve this bug we set .value to .value so the internal_value is updated with the shown, altered, data. This has to be done after the control stores the entered data in the internal_value. Since the onchange is fired when the users leaves the control, we can use onblur the correct the problem.

This is not a solution for your task but is a solution for the onchange bug.

Herman Kruisman | 2006.08.04 01:28 AM

Oh, now I get it. You're not addressing the issue in this post, your addressing the one in the original post from which this one stems:

http://ewbi.blogs.com/develops/2004/12/ie_changing_a_t.html

Got it. In that post I suggested a quick timeout to detach the onchange event from the code that changed the value. You're suggesting leave the onchange alone (verify, convert, format, etc. as needed), but rather than updating the control's content, use some other local storage (e.g., a variable) and then use the onblur to synchronize the local storage with the control's content.

In short, I agree. That's a fine approach, and one I've used as well. That particular post was really just a quickie intended to highlight an IE/FireFox difference. I had planned to write something up sometime on using a more robust model/controller framework for handling data entry/validation in the browser. Perhaps I'll get around to it sometime.

Thanks for taking the time to explain.

ewbi.develops | 2006.08.08 02:03 PM

about formula's

Azeem | 2006.10.02 02:22 AM

Good, I met this problem yestoday , I finally find this article , thank ewbi.develops .

chengyayu | 2006.11.14 07:23 AM

I have a popup window with a button. When i press this button a hidden input field located in the parent window is populated with a text value. I would like to know if it is possible to detect when the value of this input field changes. The popup window is a component. i cant mess around with its code to tell the parent window that the value of the field has changed.
I tried placing an onchange event on the hidden input field but that wouldnt work because the imput field must lose focus for the onchange event to be triggerd. any ideas.

thanks

Didier | 2007.01.11 07:14 AM

Hi Didier,

That is a tricky situation. Before making a suggestion, though, I'm wondering if you have any control over what exactly the popup window updates in the parent? Can you redirect the update to the property of a custom object? Can you address the popup window's DOM in order to attach an event handler to its button? Do you have control over the parent window, including the logic that loads the popup?

ewbi.develops | 2007.01.12 01:45 AM

I am trying to create an accessible 'jump' menu.
If JavaScript os available, the 'Go!' buttons are hidden (with CSS), and the jumpMenu script is called.
If JavaScript isn't available, a server-side cgi is used.
I have the following form:

Choose...
URL 1
URL 2
URL 3
URL 4


And the following scripts:

function onload_handler(){
var w = document.getElementsByName("jump");
for (i=0;i<(w.length);i++)
{
w[i].action="";
w[i].method="";
}

var x = document.getElementsByName("url");
for (i=0;i<(x.length);i++)
{
x[i].onchange="jumpMenu('parent',this,0)";
}

/*Hide the 'Go!' buttons - could be more than one*/
var y = document.getElementsByName("Input");
for (i=0; i<(y.length); i++)
{
y[i].style.visibility="hidden";
};

}

function jumpMenu(targ,selObj,restore){
eval(targ+".location='"+selObj.options[selObj.selectedIndex].value+"'");
if (restore) selObj.selectedIndex=0;
}

I thought this should work - but it doesn't. If I inspect the DOM, the onChange value has been changed, but it doesn't work... any suggestions?

John Salter | 2007.01.15 02:29 AM

The Form in the above post has an action of 'URL for the cgi', and the Select values are the URLs to the resource pages.


Also: On the 'Post a Comment Form, the '(no html)' looks to be associated with the 'URL' field, not the 'Comments' field

John Salter | 2007.01.15 02:34 AM

Hi John,

The problem is that the onclick property of an element is expected to be a function reference, a handler, not the name of a function to execute. I think this often causes confusion because the attribute of an element in HTML is naturally a text string describing the name of a function (or functions, or JavaScript statements, or whatever); however, when read and evaluated by the browser, these textual attribute descriptions become functions, access to which is made available via the onclick property. Here's a good explanation:

http://www.quirksmode.org/js/events_tradmod.html

Ultimately what this means is that you can't just set the SELECT element's onclick to a string containing a definition of the function you want called. You have to give the onclick property a reference to the function itself. If jumpMenu had no parameters this would be pretty simple:

x[i].onchange=jumpMenu;

However, with the addition of parameters this becomes a little trickier. No longer can you just pass a pointer to a function (sans parantheses and parameters). Instead, you'll need to construct a parameter-less anonymous function that calls your function passing the parameters it needs. The anonymous function definition will enclose variables from the current context that you want passed to the function. In this case the only non-static variable you need to pass is a reference to the SELECT element, which you're enumerating via the x array. So you can use this:

var t = x[i];
x[i].onchange = function() { jumpMenu('parent', t, 0); };

Let me know if you have any questions. Good luck.

ewbi.develops | 2007.01.15 11:09 AM

I tried your simplistic example but I am encountering problems in Firefox.

I have a popup window that contains a form that, on submit, passes a value from a textbox to a readonly textbox with the same name in the parent page, then closes. I have extended this to try and programatically trigger the onchange event on the parent textbox. In IE7 & IE6 this works perfectly but Firefox barfs in a big way.

mainForm[currEl.name].onchange is not a function

On the parent page I have an onchange event handler on the readonly text box. Basically it is just an ajax call to update a thumbnail image based on the new value in the textbox.

JS code in the popup is as follows:

function setMaster() {
var mainForm = opener.document.forms.article;
var popForm = document.forms.popForm;
var currEl;
for (var i=0; i currEl = popForm[i];
if (mainForm[currEl.name]) {
mainForm[currEl.name].value = popForm[currEl.name].value;
mainForm[currEl.name].onchange();
self.close();
}
}
if (opener && !opener.closed) opener.focus();
self.close();
}

Any idea why this works fine in IE yet not in Firefox? Normally these anomalies are the other way round :P

Adam Frame | 2007.03.14 10:06 AM

Great, the comments box cut up my function ... the for loop (for (var i=0; ...) basically just iterates through all elements in popForm. The problem is only with mainForm[currEl.name]onchange();

Adam Frame | 2007.03.14 10:14 AM

Hi Adam,

How are you attaching the handler function to the onchange event of the main form's textbox (in code, declarative, etc.)? And, what does Firefox tell you typeof(mainForm[currEl.name].onchange) is?

ewbi.develops | 2007.03.15 04:17 AM

I changed my function a little to include a check for onchange and it appears to be working. As I only had one field to update I removed the for loop.

Modified function below (fingers crossed) ...

function setMaster() {
var mainForm = opener.document.forms.article;
var popForm = document.forms.popForm;
var theElement = mainForm['imageID'];
if (theElement) {
theElement.value = popForm['imageID'].value;
if (theElement.onchange) {
theElement.onchange();
}
self.close();
}
if (opener && !opener.closed) opener.focus();
self.close();
}

This did indeed trigger the onchange event and I was able (by adding a little 100ms timeout) to use ajax to update the thumbnail.

Thanks for your response. I guess I'm not the only one who works this late.

Adam Frame | 2007.03.15 08:01 PM

I have problem like, I have userdefined function as databindings(), I want to call a textbox change event in that userdefined fucntion.

I am using VS 2005, c# language

sudha | 2007.05.09 12:40 AM

onblur="this.value=this.value" is INDEED the fix. So simple for such a difficult problem to track down.

Thanks Herman and ewbi.develops for the solution.

Robert Hirn | 2007.05.27 04:11 PM

This doesn't seem to work if I add the 'onchange' event using javascript.
[code]


document.getElementById('textInput').onchange = function() {
alert('changed');
};

[/code]
For whatever reason, the 'onchange' property for the element comes back as 'undefined' in the code?

Jon | 2007.09.24 07:28 AM

Hi Jon,

I just tried it and it works for me in FF v2 and IE v7. Are you sure your code is being executed after the page loads so that the element "textInput" exists? How about changing it to something like this just to be sure:

var el = document.getElementById('textInput');
if (el)
el.onchange = function() { alert('changed'); }
else
alert("textInput not found");

ewbi.develops | 2007.09.24 12:57 PM

This is my problem. I have a textbox on whose onchange event i need to trigger an event. But this textboxes value is getting modified by a javascript whose code i cannot change. What do i do??? how do i trigger an onchange when that javascript is executed when im not able to change that code??

divya | 2007.09.27 04:28 AM

divya,

In that case you're going to have to setup a recurring timeout to watch the textbox for changes. Here's an explanation of the setTimeout and setInterval functions:

http://www.elated.com/articles/javascript-timers-with-settimeout-and-setinterval/

Let me know if you need further help. Good luck!

ewbi.develops | 2007.09.27 10:49 AM

I found that element.onchange() makes problems if I call functions inside objects in onchange function.

.... // wont work

but this will work
.... // works

document.getElementById("mysl").onchange()

klemen | 2008.01.05 04:23 PM

I found that element.onchange() makes problems if I call functions inside objects in onchange function.

onchange="myObj.fn()" // wont work

but this will work
onchange="fn()" // works

klemen | 2008.01.05 04:25 PM

My posts can be deleted. My problem was somewere else ...

klemen | 2008.01.05 04:39 PM

No problem, klemen - thanks for letting us know!

ewbi.develops | 2008.01.05 08:27 PM

I had a problem similar to divya's--where I couldn't change the code of the value-editing component, but needed something to happen when the value was changed programmatically. I used your setTimeout solution--works well for now. Thanks!

Curtis | 2008.07.30 01:48 PM

In IE it is simple, u can use onPropertyChange

Sreejith | 2008.11.07 02:15 PM

Sreejith, beautiful! Years and years of HTML/DOM coding and I knew not of this valuable event. This is worth an entirely separate post. Thank you!

ewbi.develops | 2008.11.12 10:27 AM

Excelente

Eduardo | 2009.02.02 09:16 AM

even if i use document.getelementbyid("dayofweek").value=day..it doesn't work.. i'm using IE6

bujji | 2009.09.23 07:30 AM

even if i use document.getelementbyid("dayofweek").value=elt..it doesn't work.. i'm using IE6

bujji | 2009.09.23 07:31 AM


function GetDay(dateStr)
{
if(CheckFrenchDate(dateStr))
{
var date = FrenchToDate(dateStr);
var numDay = GetNumDayOfWeek( D.getDate(), D.getMonth()+1, D.getFullYear());
return GetDayOfWeekFrench(numDay);
}
else
return "";
}

function SetDay(dateStr)
{
var day = GetDay(dateStr);
alert(day);
var elt = day
document.getElementbyid('DayOfWeek')= elt;
alert(elt);
}

hello, it got deleted..

i want to display name of the day passed from day in another text field in id 'day of week'..the value is already in day.but isn't working..can anyone help me..

bujji | 2009.09.23 07:35 AM

Bujji, I can't say whether there are other things wrong, but minimally, your setter line needs to read:

document.getElementById('DayOfWeek').value= elt;

Note the proper capitalization of getElementById and assignment to the element's value property.

Good luck.

ewbi.develops | 2009.09.23 08:56 AM

Can you please translate this line into C# code.
if (typeof(textbox.onchange) == "function") textbox.onchange();

Thank you
Saquib

Saquib | 2013.09.06 07:13 AM

Hi Saquib,

I'm not sure how to address your question. That particular bit of Javascript refers to a browser DOM object, textbox, whose onchange property may refer to the name of a function or the function itself. C# doesn't run in a browser and so can't access the DOM, and C# doesn't have first-class functions like Javascript, so can't reference them in this same way, except as delegates.

Is there something in particular you're hoping to achieve? Perhaps we can figure out another way to do it.

ewbi.develops | 2013.09.06 05:06 PM

comment

name | 2016.01.18 12:35 AM


TrackBack

TrackBack URL:  http://www.typepad.com/services/trackback/6a00d8341c7bd453ef00d834da28f669e2

Listed below are links to weblogs that reference Programmatic Value Changes and the Onchange Event: