jQuery UI, knockout.js, and Custom HTML Events

Thursday, May 16th 2013

Ok, so, I’m a purist. I like things to do their job, do it well, and not reach beyond their boundaries of functionality. In this case, I refer to jQuery UI and knockout.js as the things under conideration.

I think that each library has a strength that I want to use in my Web application:

jQuery UI
Used to create widgets and handle events like mouse clicks.
knockout.js
Used to bind values from JavaScript objects to HTML nodes (and back again).

The problem

With that in mind, I encountered a “problem” with getting the two to work seamlessly with one another specifically around using the jQuery UI Datepicker. When a user selects a date from the calendar control, the Datepicker sets the value of the associated INPUT node with the selected date.

How does it do that?

Well, it does it with JavaScript. Duh.

When jQuery sets the value of the INPUT node with JavaScript, the browser does not fire the onchange event. Because of that, knockout.js has no idea that the value changed. The view model does not get updated. Something becomes rotten in the state of Denmark.

I don’t want jQuery UI to know about my knockout.js bindings and the other way around. I want the browser to act the way as if a user had typed into the field.

The solution

My solution comes in the form of creating an event in the browser similar to the one that the browser would raise if the user had typed in the value. I want to fire an onchange event for that INPUT node. Turns out that the browsers can do that. Of course, it depends on the browser for the specific implementation. (Yes, I’m looking at you, Internet Explorer!)

First, I want the Datepicker to fire the event when the user selects a date.

1
2
3
4
5
6
$('input.date')
.datepicker({
onSelect: function() {
evt.fire(this, 'change');
}
});

Now, where did that evt.fire method come from? I imported a module using RequireJS and bound it to the evt variable. Here’s what that module looks like.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
define(function() {
var o = {
fire: null
};

// This is for old IE
if(document.createEventObject && !document.createEvent) {
o.fire = function(element, event) {
var e = document.createEventObject();
return element.fireEvent('on' + event, e);
};
} else {
o.fire = function(element, event) {
var e;
try {
// This is for new IE
e = document.createEvent('HTMLEvents');
} catch(e) {
// This is for everyone else
e = document.createEvent('UIEvents');
}
e.initEvent(event, true, true);
return !element.dispatchEvent(e);
};
}

return o;
});

Now, the onchange event fires when jQuery UI Datepicker sets the value of the field and knockout.js now knows to get the newly-entered value.

References

document.createEventObject

document.createEvent(‘HTMLEvents’)

document.createEvent(‘UIEvents’)