15 Jul 2011

Ahhh, this is the life: struggling for days with some obscure IE bug.

I have an onchange attribute with a jQuery AJAX call on a HTML dropdown that gives strange errors. But only in IE (8, didn't try it in other versions). When replacing the AJAX call with an alert, like so:

<select onchange="alert('onchange');">...</select>

it becomes clear that the onchange is triggered twice. This was also confirmed in the IE Developer Tools window, but I can't use that to debug further.

After gradually commenting out included javascript libraries, I found the cause must have something to do with a an jQuery bind of the onchange event. Besides the onchange in the HTML, this library initializes all select boxes to have some alternative styling. To do that, jQuery also binds an onchange using the .change function.

Apparently, after this .change binding is done, IE8 triggers the HTML onchange, twice.

My first approach to solve this generically is to automatically convert HTML onchange attributes to jQuery .change bindings. I tried to "copy" the onchange from the HTML attribute to jQuery using:

var x = dropdown.attr("onchange");
dropdown.change(
function()
  {
    eval(x);
  }
)

Unfortunately this gave some unexpected behaviour in IE7, because where dropdown.attr("change") returns this in IE7:

alert("onchange")

In FF en IE8 it returns:

function()
{
  alert("onchange");
}

In other words: don't mix the HTML onchange attribute and jQuery onchange binding (unless you don't care to support IE).

Now I've fully migrated all onchange behaviour to multiple jQuery binds, only leaving a CSS class (e.g. 'alert') in the HTML, and then tracking dropdowns with that class:

jQuery.each(jQuery("select.alert"), function(){
  jQuery(this).unbind("change.alert");
  jQuery(this).bind("change.alert", function(){
    alert("onchange");
  });
});

I am using a namespaced bind (the '.alert' suffix to the 'change' string in the more generic .bind function, instead of using just .change). This makes it possible to just unbind this onchange function, and leave other onchange functions bound. This is required, because I also do an unbind right before the bind. This is to prevent multiple binds after the AJAX request.