How can I get Knockout JS to data-bind on keypress instead of lost-focus?

Solution 1:

<body>
        <p>First name: <input data-bind="value: firstName, valueUpdate: 'afterkeydown'" /></p>
        <p>Last name: <input data-bind="value: lastName, valueUpdate: 'afterkeydown'" /></p>
        <h2>Hello, <span data-bind="text: fullName"> </span>!</h2>
</body>

From the documentation

Additional parameters

  • valueUpdate

    If your binding also includes a parameter called valueUpdate, this defines which browser event KO should use to detect changes. The following string values are the most commonly useful choices:

    • "change" (default) - updates your view model when the user moves the focus to a different control, or in the case of elements, immediately after any change

    • "keyup" - updates your view model when the user releases a key

    • "keypress" - updates your view model when the user has typed a key. Unlike keyup, this updates repeatedly while the user holds a key down

    • "afterkeydown" - updates your view model as soon as the user begins typing a character. This works by catching the browser’s keydown event and handling the event asynchronously.

Of these options, "afterkeydown" is the best choice if you want to keep your view model updated in real-time.

Solution 2:

In version 3.2 you can simply use textinput binding.:

<input data-bind="textInput: userName" />

It does two important things:

  • make immediate updates
  • handles browser differences for cut, drag, autocomplete ...

So no need for additional modules, custom controls and other things.

Solution 3:

If you want it to do updates on afterkeydown "by default," you could inject the valueUpdate binding in the value binding handler. Just supply a new allBindingsAccessor for the handler to use that includes afterkeydown.

(function () {
    var valueHandler = ko.bindingHandlers.value;
    var getInjectValueUpdate = function (allBindingsAccessor) {
        var AFTERKEYDOWN = 'afterkeydown';
        return function () {
            var allBindings = ko.utils.extend({}, allBindingsAccessor()),
                valueUpdate = allBindings.valueUpdate;

            if (valueUpdate === undefined) {
                return ko.utils.extend(allBindings, { valueUpdate: AFTERKEYDOWN });
            } else if (typeof valueUpdate === 'string' && valueUpdate !== AFTERKEYDOWN) {
                return ko.utils.extend(allBindings, { valueUpdate: [valueUpdate, AFTERKEYDOWN] });
            } else if (typeof valueUpdate === 'array' && ko.utils.arrayIndexOf(valueUpdate, AFTERKEYDOWN) === -1) {
                valueUpdate = ko.utils.arrayPushAll([AFTERKEYDOWN], valueUpdate);
                return ko.utils.extend(allBindings, {valueUpdate: valueUpdate});
            }
            return allBindings;
        };
    };
    ko.bindingHandlers.value = {
        // only needed for init
        'init': function (element, valueAccessor, allBindingsAccessor) {
            allBindingsAccessor = getInjectValueUpdate(allBindingsAccessor);
            return valueHandler.init(element, valueAccessor, allBindingsAccessor);
        },
        'update': valueHandler.update
    };
} ());

If you're not comfortable "overriding" the value binding, you could give the overriding custom binding a different name and use that binding handler.

ko.bindingHandlers.realtimeValue = { 'init':..., 'update':... };

demo

A solution like this would be suitable for Knockout version 2.x. The Knockout team has fleshed out a more complete binding for text-like inputs through the textInput binding in Knockout version 3 and up. It was designed to handle all text input methods for text inputs and textarea. It will even handle real time updating which effectively renders this approach obsolete.