192

This example of knockout js works so when you edit a field and press TAB, the viewmodel data and hence the text below the fields is updated.

How can I change this code so that the viewmodel data is updated every keypress?

alt text

<!doctype html>
<html>
    <title>knockout js</title>
    <head>
        <script type="text/javascript" src="js/knockout-1.1.1.debug.js"></script>
        <script type="text/javascript">
        window.onload= function() {

            var viewModel = {
                firstName : ko.observable("Jim"),
                lastName : ko.observable("Smith")
            };
            viewModel.fullName = ko.dependentObservable(function () {
                return viewModel.firstName() + " " + viewModel.lastName();
            });

            ko.applyBindings(viewModel);
       }
        </script>
    </head>
    <body>
        <p>First name: <input data-bind="value: firstName" /></p>
        <p>Last name: <input data-bind="value: lastName" /></p>
        <h2>Hello, <span data-bind="text: fullName"> </span>!</h2>
    </body>
</html>
Shadow The GPT Wizard
  • 66,030
  • 26
  • 140
  • 208
Edward Tanguay
  • 189,012
  • 314
  • 712
  • 1,047

4 Answers4

301
<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.

James Monger
  • 10,181
  • 7
  • 62
  • 98
Sergei Golos
  • 4,342
  • 1
  • 18
  • 21
  • 1
    awesome, what are the other events, valueUpdate observes? – Vin Jan 15 '11 at 23:58
  • 9
    from the docs in knockout.js: // The syntax "after" means "run the handler asynchronously after the event" // This is useful, for example, to catch "keydown" events after the browser has updated the control – John Wright Jan 19 '11 at 21:21
  • 4
    Is there anyway to get valueUpdate:'afterkeydown' to be the _default_? – Aaron Shafovaloff Sep 08 '12 at 03:32
  • 2
    for some input boxes the browser shows "auto-populate" values ( like if the input field is named "firstname" ). selecting from the auto-populate doesnt work with "afterkeydown". is there a way to catch this? – Anton Sep 09 '12 at 00:01
  • 2
    @Anton In addition to auto-populate values, there are also on-screen keyboard clicks and mouse paste events that should be caught. Using `afterkeydown` is a bad solution. – John Kurlak Jan 02 '13 at 22:01
  • @JohnKurlak Good point about the mouse paste events (you can handle those with `afterpaste` I think) but what on-screen keyboards are you talking about? iOS and Android both respond to `keydown`, what devices don't? – Matt Browne Feb 09 '13 at 16:12
  • @MattB. The event is just called "paste," and that should work in most scenarios. I was just talking about the on-screen keyboard on a regular Windows machine. However, when I tested the on-screen keyboard in Windows 8 just now, it seemed to register. My bad! – John Kurlak Feb 10 '13 at 21:06
  • I just learned about HTML5's `oninput` event, which covers all the bases. I started an issue for Knockout recommending a feature to allow an `input` value for valueUpdate that would degrade gracefully: https://github.com/SteveSanderson/knockout/issues/836 – Matt Browne Feb 11 '13 at 18:38
  • How to throttle the binding after keydown? – Linc Abela Jul 11 '14 at 00:05
  • 1
    I have found the `input` event (IE9+) to work very nicely for this purpose, as it will also trigger if you paste something in the text field by right click menu, for instance. http://jsfiddle.net/40fumdem/ – xec Aug 20 '14 at 07:34
  • 2
    Author of this answer, please update to also include Salvador Dali's answer, textInput added as of 3.2 is a better solution as it has better compatibility with mobile browsers (things like auto complete work better) – Michael Crook Oct 08 '14 at 22:48
87

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.

Salvador Dali
  • 214,103
  • 147
  • 703
  • 753
  • This works really well and is exactly what I needed to replace the value: field, valueUpdate: 'input change' that I had all through my app... :) thanks. – Tod Thomson Nov 07 '14 at 01:44
  • Is there a way to capture the event when a user clicks on the 'x' icon on the new HTML5 search input field and act as in the case the input is empty ? – Codrina Valo Aug 06 '16 at 15:39
35

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.

Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272
  • 2
    This way makes my markup a little tidier. Also, in addition to 'afterkeydown', 'input' and 'paste' events can be added to handle paste/drag-and-drop actions. – bob Apr 25 '13 at 21:09
  • In the above solution I think that "typeof valueUpdated === 'array'" needs to be replaced with "Object.prototype.toString.call(valueUpdate) === '[object Array]'". See http://stackoverflow.com/questions/4775722/javascript-check-if-object-is-array – daveywc May 24 '13 at 21:46
20

Jeff Mercado's answer is fantastic, but unfortunately broken with Knockout 3.

But I found the answer suggested by the ko devs while working through Knockout 3 changes. See the bottom comments at https://github.com/knockout/knockout/pull/932. Their code:

//automatically add valueUpdate="afterkeydown" on every value binding
(function () {
    var getInjectValueUpdate = function (allBindings) {
        return {
            has: function (bindingKey) {
                return (bindingKey == 'valueUpdate') || allBindings.has(bindingKey);
            },
            get: function (bindingKey) {
                var binding = allBindings.get(bindingKey);
                if (bindingKey == 'valueUpdate') {
                    binding = binding ? [].concat(binding, 'afterkeydown') : 'afterkeydown';
                }
                return binding;
            }
        };
    };

    var valueInitHandler = ko.bindingHandlers.value.init;
    ko.bindingHandlers.value.init = function (element, valueAccessor, allBindings, viewModel, bindingContext) {
        return valueInitHandler(element, valueAccessor, getInjectValueUpdate(allBindings), viewModel, bindingContext);
    };
}());

http://jsfiddle.net/mbest/GKJnt/

Edit Ko 3.2.0 now has a more complete solution with the new "textInput" binding. See SalvidorDali's answer

RockResolve
  • 1,423
  • 21
  • 29
  • @GrahamT in KO 3.2 you do not even need it. It is [just one line](http://stackoverflow.com/a/25493265/1090562) – Salvador Dali Sep 09 '14 at 21:25
  • Very nice for those of us on KO 3.2 who didn't know about TextInput when we started, and can't update every input in the whole system! – cscott530 Dec 22 '16 at 15:54