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)

Creating custom bindings that support virtual elements

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

Knockout’s control flow bindings (e.g., if and foreach) can be applied not only to regular DOM elements, but also to “virtual” DOM elements defined by a special comment-based syntax. For example:

<ul>
    <li class="heading">My heading</li>
    <!-- ko foreach: items -->
        <li data-bind="text: $data"></li>
    <!-- /ko -->
</ul>

Custom bindings can work with virtual elements too, but to enable this, you must explicitly tell Knockout that your binding understands virtual elements, by using the ko.virtualElements.allowedBindings API.

Example

To get started, here’s a custom binding that randomises the order of DOM nodes:

ko.bindingHandlers.randomOrder = {
    init: function(elem, valueAccessor) {
        // Pull out each of the child elements into an array
        var childElems = [];
        while(elem.firstChild)
            childElems.push(elem.removeChild(elem.firstChild));

        // Put them back in a random order
        while(childElems.length) {
            var randomIndex = Math.floor(Math.random() * childElems.length),
                chosenChild = childElems.splice(randomIndex, 1);
            elem.appendChild(chosenChild[0]);
        }
    }
};

This works nicely with regular DOM elements. The following elements will be shuffled into a random order:

<div data-bind="randomOrder: true">
    <div>First</div>
    <div>Second</div>
    <div>Third</div>
</div>

However, it does not work with virtual elements. If you try the following:

<!-- ko randomOrder: true -->
    <div>First</div>
    <div>Second</div>
    <div>Third</div>
<!-- /ko -->

… then you’ll get the error The binding 'randomOrder' cannot be used with virtual elements. Let’s fix this. To make randomOrder usable with virtual elements, start by telling Knockout to allow it. Add the following:

ko.virtualElements.allowedBindings.randomOrder = true;

Now there won’t be an error. However, it still won’t work properly, because our randomOrder binding is coded using normal DOM API calls (firstChild, appendChild, etc.) which don’t understand virtual elements. This is the reason why KO requires you to explicitly opt in to virtual element support: unless your custom binding is coded using virtual element APIs, it’s not going to work properly!

Let’s update the code for randomOrder, this time using KO’s virtual element APIs:

ko.bindingHandlers.randomOrder = {
    init: function(elem, valueAccessor) {
        // Build an array of child elements
        var child = ko.virtualElements.firstChild(elem),
            childElems = [];
        while (child) {
            childElems.push(child);
            child = ko.virtualElements.nextSibling(child);
        }

        // Remove them all, then put them back in a random order
        ko.virtualElements.emptyNode(elem);
        while(childElems.length) {
            var randomIndex = Math.floor(Math.random() * childElems.length),
                chosenChild = childElems.splice(randomIndex, 1);
            ko.virtualElements.prepend(elem, chosenChild[0]);
        }
    }
};

Notice how, instead of using APIs like domElement.firstChild, we’re now using ko.virtualElements.firstChild(domOrVirtualElement). The randomOrder binding will now correctly work with virtual elements, e.g., <!-- ko randomOrder: true -->...<!-- /ko -->.

Also, randomOrder will still work with regular DOM elements, because all of the ko.virtualElements APIs are backwardly compatible with regular DOM elements.

Virtual Element APIs

Knockout provides the following functions for working with virtual elements.

  • ko.virtualElements.allowedBindings

    An object whose keys determine which bindings are usable with virtual elements. Set ko.virtualElements.allowedBindings.mySuperBinding = true to allow mySuperBinding to be used with virtual elements.

  • ko.virtualElements.emptyNode(containerElem)

    Removes all child nodes from the real or virtual element containerElem (cleaning away any data associated with them to avoid memory leaks).

  • ko.virtualElements.firstChild(containerElem)

    Returns the first child of the real or virtual element containerElem, or null if there are no children.

  • ko.virtualElements.insertAfter(containerElem, nodeToInsert, insertAfter)

    Inserts nodeToInsert as a child of the real or virtual element containerElem, positioned immediately after insertAfter (where insertAfter must be a child of containerElem).

  • ko.virtualElements.nextSibling(node)

    Returns the sibling node that follows node in its real or virtual parent element, or null if there is no following sibling.

  • ko.virtualElements.prepend(containerElem, nodeToPrepend)

    Inserts nodeToPrepend as the first child of the real or virtual element containerElem.

  • ko.virtualElements.setDomNodeChildren(containerElem, arrayOfNodes)

    Removes all child nodes from the real or virtual element containerElem (in the process, cleaning away any data associated with them to avoid memory leaks), and then inserts all of the nodes from arrayOfNodes as its new children.

Notice that this is not intended to be a complete replacement to the full set of regular DOM APIs. Knockout provides only a minimal set of virtual element APIs to make it possible to perform the kinds of transformations needed when implementing control flow bindings.