Getting started

  1. How KO works and what benefits it brings
  2. Downloading and installing

Observables

  1. Creating view models with observables
  2. Working with observable arrays

Computed observables

  1. Using computed observables
  2. Writable computed observables
  3. How dependency tracking works
  4. Pure computed observables
  5. Reference

Bindings

Controlling text and appearance

  1. The visible and hidden bindings
  2. The text binding
  3. The html binding
  4. The class and css bindings
  5. The style binding
  6. The attr binding

Control flow

  1. The foreach binding
  2. The if and ifnot bindings
  3. The with and using bindings
  4. The let binding
  5. The component binding
  6. Binding lifecycle events

Working with form fields

  1. The click binding
  2. The event binding
  3. The submit binding
  4. The enable and disable bindings
  5. The value binding
  6. The textInput binding
  7. The hasFocus binding
  8. The checked binding
  9. The options binding
  10. The selectedOptions binding
  11. The uniqueName binding

Rendering templates

  1. The template binding

Binding syntax

  1. The data-bind syntax
  2. The binding context

Creating custom bindings

  1. Creating custom bindings
  2. Controlling descendant bindings
  3. Supporting virtual elements
  4. Custom disposal logic
  5. Preprocessing: Extending the binding syntax

Components

  1. Overview: What components and custom elements offer
  2. Defining and registering components
  3. The component binding
  4. Using custom elements
  5. Advanced: Custom component loaders

Further techniques

  1. Loading and saving JSON data
  2. Extending observables
  3. Deferred updates
  4. Rate-limiting observables
  5. Unobtrusive event handling
  6. Using fn to add custom functions
  7. Microtasks
  8. Asynchronous error handling

Plugins

  1. The mapping plugin

More information

  1. Browser support
  2. Getting help
  3. Links to tutorials & examples
  4. Usage with AMD using RequireJs (Asynchronous Module Definition)

Extending Knockout's binding syntax using preprocessing

Note: This is an advanced technique, typically used only when creating libraries of reusable bindings or extended syntaxes. It’s not something you’ll normally need to do when building applications with Knockout.

With binding preprocessing, developers can define custom syntaxes by providing callbacks that rewrite DOM nodes and binding strings during the binding process.

Preprocessing binding strings

You can hook into Knockout’s logic for interpreting data-bind attributes by providing a binding preprocessor for a specific binding (such as click, visible, or any custom binding).

To do this, attach a preprocess function to the binding handler object:

ko.bindingHandlers.yourBindingHandler.preprocess = function(stringFromMarkup) {
    // Return stringFromMarkup if you don't want to change anything, or return
    // some other string if you want Knockout to behave as if that was the
    // syntax provided in the original HTML
}

See later on this page for an API reference.

Example 1: Setting a default value for a binding

If you leave off the value of a binding, it’s bound to undefined by default. If you want to have a different default value for a binding, you can do so with a preprocessor. For example, you can allow uniqueName to be bound without a value by making its default value true:

ko.bindingHandlers.uniqueName.preprocess = function(val) {
    return val || 'true';
}

Now you can bind it like this:

<input data-bind="value: someModelProperty, uniqueName" />

If your binding uses a preprocess function but should still have undefined as a default value, the function should return the string "undefined" rather than an undefined value. Returning undefined will remove the binding instead.

Example 2: Binding expressions to events

If you’d like to be able to bind expressions to click events (rather than a function reference as Knockout expects), you can set up a preprocessor for the click handler to support this syntax:

ko.bindingHandlers.click.preprocess = function(val) {
    return 'function($data,$event){ ' + val + ' }';
}

Now you can bind click like this:

<button type="button" data-bind="click: myCount(myCount()+1)">Increment</button>

Binding preprocessor reference

  • ko.bindingHandlers.<name>.preprocess(value, name, addBindingCallback)

    If defined, this function will be called for each <name> binding before the binding is evaluated.

    Parameters:

    • value: the syntax associated with the binding value before Knockout attempts to parse it (e.g., for yourBinding: 1 + 1, the associated value is "1 + 1" as a string). If the binding was given without a value, this will be undefined.

    • name: the name of the binding (e.g., for yourBinding: 1 + 1, the name is "yourBinding" as a string).

    • addBinding: a callback function you can optionally use to insert another binding on the current element. This requires two parameters, name and value. For example, inside your preprocess function, call addBinding('visible', 'acceptsTerms()'); to make Knockout behave as if the element had a visible: acceptsTerms() binding on it.

    Return value:

    Your preprocess function must return the new string value to be parsed and passed to the binding, or return undefined to remove the binding.

    For example, if you return value + ".toUpperCase()" as a string, then yourBinding: "Bert" would be interpreted as if the markup contained yourBinding: "Bert".toUpperCase(). Knockout will parse the returned value in the normal way, so it has to be a legal JavaScript expression.

    Don’t return non-string values. That wouldn’t make sense, because markup is always a string.

Preprocessing DOM nodes

You can hook into Knockout’s logic for traversing the DOM by providing a node preprocessor. This is a function that Knockout will call once for each DOM node that it walks over, both when the UI is first bound, and later when any new DOM subtrees are injected (e.g., via a foreach binding).

To do this, define a preprocessNode function on your binding provider:

ko.bindingProvider.instance.preprocessNode = function(node) {
    // Use DOM APIs such as setAttribute to modify 'node' if you wish.
    // If you want to leave 'node' in the DOM, return null or have no 'return' statement.
    // If you want to replace 'node' with some other set of nodes,
    //    - Use DOM APIs such as insertChild to inject the new nodes
    //      immediately before 'node'
    //    - Use DOM APIs such as removeChild to remove 'node' if required
    //    - Return an array of any new nodes that you've just inserted
    //      so that Knockout can apply any bindings to them
}

See later on this page for an API reference.

Example 3: Virtual template elements

If you commonly include template content using virtual elements, the normal syntax can feel a bit verbose. Using preprocessing, you can add a new template format that uses a single comment:

ko.bindingProvider.instance.preprocessNode = function(node) {
    // Only react if this is a comment node of the form <!-- template: ... -->
    if (node.nodeType == 8) {
        var match = node.nodeValue.match(/^\s*(template\s*:[\s\S]+)/);
        if (match) {
            // Create a pair of comments to replace the single comment
            var c1 = document.createComment("ko " + match[1]),
                c2 = document.createComment("/ko");
            node.parentNode.insertBefore(c1, node);
            node.parentNode.replaceChild(c2, node);

            // Tell Knockout about the new nodes so that it can apply bindings to them
            return [c1, c2];
        }
    }
}

Now you can include a template in your view like this:

<!-- template: 'some-template' -->

Preprocessing Reference

  • ko.bindingProvider.instance.preprocessNode(node)

    If defined, this function will be called for each DOM node before bindings are processed. The function can modify, remove, or replace node. Any new nodes must be inserted immediately before node, and if any nodes were added or node was removed, the function must return an array of the new nodes that are now in the document in place of node.