JavaScript MVVM library that makes it easier to create rich, responsive UIs with automatic UI synchronization through observable data binding
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Declarative binding system that connects view models to DOM elements with automatic synchronization and a rich set of built-in binding handlers. The binding system uses data-bind attributes to establish connections between HTML elements and JavaScript data.
Functions to apply data bindings between view models and DOM elements.
/**
* Apply bindings to connect view model to DOM
* @param viewModel - The view model object
* @param rootNode - Optional root DOM node (defaults to document.body)
*/
function applyBindings(viewModel: any, rootNode?: Node): void;
/**
* Apply bindings to descendant nodes only
* @param viewModel - The view model object
* @param rootNode - Root DOM node
*/
function applyBindingsToDescendants(viewModel: any, rootNode: Node): void;
/**
* Apply bindings to a specific DOM node
* @param node - Target DOM node
* @param bindings - Binding specifications object
* @param viewModel - The view model object
*/
function applyBindingsToNode(node: Node, bindings: object, viewModel: any): void;
/**
* Apply binding accessors to a specific DOM node
* @param node - Target DOM node
* @param bindingAccessors - Binding accessor functions
* @param viewModel - The view model object
*/
function applyBindingAccessorsToNode(node: Node, bindingAccessors: BindingAccessors, viewModel: any): void;Usage Examples:
import ko from "knockout";
// Basic binding application
function ViewModel() {
this.message = ko.observable("Hello World");
}
ko.applyBindings(new ViewModel());
// Binding to specific element
const element = document.getElementById("myDiv");
ko.applyBindings(new ViewModel(), element);
// Programmatic binding
const node = document.getElementById("target");
ko.applyBindingsToNode(node, {
text: "message",
click: "handleClick"
}, new ViewModel());Functions to access binding context and data from DOM nodes.
/**
* Get the binding context for a DOM node
* @param node - DOM node
* @returns Binding context or undefined
*/
function contextFor(node: Node): BindingContext | undefined;
/**
* Get the data object for a DOM node
* @param node - DOM node
* @returns Data object or undefined
*/
function dataFor(node: Node): any;Usage Examples:
import ko from "knockout";
const element = document.getElementById("bound-element");
const context = ko.contextFor(element);
const data = ko.dataFor(element);
console.log(context.$data); // View model data
console.log(data); // Same as context.$dataObject that provides context information available in binding expressions.
interface BindingContext<T = any> {
/** The current data object */
$data: T;
/** The root view model */
$root: any;
/** The parent data object */
$parent?: any;
/** Array of all parent data objects */
$parents: any[];
/** Observable index for items in foreach binding */
$index?: Observable<number>;
/** Reference to parent binding context */
$parentContext?: BindingContext<any>;
/** Current component instance (if inside component) */
$component?: any;
/** Reference to ko object */
ko: any;
/** Create an extended context with additional properties */
extend(properties: object): BindingContext<T>;
extend(properties: (self: BindingContext<T>) => object): BindingContext<T>;
/** Create a child context for nested data */
createChildContext<U>(
dataItem: U | Observable<U>,
dataItemAlias?: string,
extendCallback?: (context: BindingContext<U>) => void
): BindingContext<U>;
}Registry and access for binding handlers.
/**
* Registry of all binding handlers
*/
const bindingHandlers: BindingHandlers;
/**
* Get a specific binding handler
* @param name - Binding handler name
* @returns Binding handler or undefined
*/
function getBindingHandler(name: string): BindingHandler | undefined;interface BindingHandler<T = any> {
/** Initialize the binding (called once) */
init?(
element: any,
valueAccessor: () => T,
allBindings: AllBindings,
viewModel: any,
bindingContext: BindingContext
): void | { controlsDescendantBindings: boolean };
/** Update the binding (called when dependencies change) */
update?(
element: any,
valueAccessor: () => T,
allBindings: AllBindings,
viewModel: any,
bindingContext: BindingContext
): void;
/** Array of binding names that must be processed after this one */
after?: string[];
/** Preprocess binding value before parsing */
preprocess?(
value: string | undefined,
name: string,
addBinding: (name: string, value: any) => void
): string | undefined | void;
}
interface AllBindings {
/** Get all bindings as object */
(): any;
/** Get specific binding value */
get(name: string): any;
get<T>(name: string): T;
/** Check if binding exists */
has(name: string): boolean;
}
interface BindingAccessors {
[name: string]: () => any;
}Bindings that control element text content, visibility, and styling.
interface BindingHandlers {
/** Set element text content */
text: {
update(element: Node, valueAccessor: () => string): void;
};
/** Set element HTML content */
html: {
update(element: Node, valueAccessor: () => string): void;
};
/** Control element visibility */
visible: {
update(element: HTMLElement, valueAccessor: () => any): void;
};
/** Control element visibility (inverse of visible) */
hidden: {
update(element: HTMLElement, valueAccessor: () => any): void;
};
/** Set CSS class name */
class: {
update(element: HTMLElement, valueAccessor: () => string): void;
};
/** Set CSS classes based on object properties */
css: {
update(element: HTMLElement, valueAccessor: () => object | string): void;
};
/** Set inline styles */
style: {
update(element: HTMLElement, valueAccessor: () => object): void;
};
/** Set element attributes */
attr: {
update(element: HTMLElement, valueAccessor: () => object): void;
};
}Usage Examples:
<!-- Text and HTML content -->
<span data-bind="text: message"></span>
<div data-bind="html: htmlContent"></div>
<!-- Visibility -->
<div data-bind="visible: isVisible">Visible when true</div>
<div data-bind="hidden: isHidden">Hidden when true</div>
<!-- CSS classes and styles -->
<div data-bind="css: { active: isActive, disabled: !isEnabled }"></div>
<div data-bind="style: { color: textColor, fontSize: fontSize() + 'px' }"></div>
<!-- Attributes -->
<img data-bind="attr: { src: imageUrl, alt: imageDescription }" />Bindings that control conditional rendering and iteration.
interface BindingHandlers {
/** Render template for each array item */
foreach: {
init(
element: Node,
valueAccessor: () => any[] | Observable<any[]>,
allBindings: AllBindings,
viewModel: any,
bindingContext: BindingContext
): { controlsDescendantBindings: boolean };
};
/** Conditionally include content */
if: {
init(
element: Node,
valueAccessor: () => any,
allBindings: AllBindings,
viewModel: any,
bindingContext: BindingContext
): { controlsDescendantBindings: boolean };
};
/** Conditionally exclude content */
ifnot: {
init(
element: Node,
valueAccessor: () => any,
allBindings: AllBindings,
viewModel: any,
bindingContext: BindingContext
): { controlsDescendantBindings: boolean };
};
/** Change binding context */
with: {
init(
element: Node,
valueAccessor: () => any,
allBindings: AllBindings,
viewModel: any,
bindingContext: BindingContext
): { controlsDescendantBindings: boolean };
};
/** Create local variable bindings */
let: {
init(
element: Node,
valueAccessor: () => object,
allBindings: AllBindings,
viewModel: any,
bindingContext: BindingContext
): { controlsDescendantBindings: boolean };
};
/** Alias for 'with' binding */
using: {
init(
element: Node,
valueAccessor: () => any,
allBindings: AllBindings,
viewModel: any,
bindingContext: BindingContext
): { controlsDescendantBindings: boolean };
};
}Usage Examples:
<!-- Foreach binding -->
<ul data-bind="foreach: items">
<li data-bind="text: $data"></li>
</ul>
<div data-bind="foreach: { data: people, as: 'person' }">
<p>Name: <span data-bind="text: person.name"></span></p>
<p>Index: <span data-bind="text: $index"></span></p>
</div>
<!-- Conditional bindings -->
<div data-bind="if: showDetails">
<p>Details content here</p>
</div>
<div data-bind="ifnot: isLoading">
<p>Content loaded</p>
</div>
<!-- Context binding -->
<div data-bind="with: selectedUser">
<p>Name: <span data-bind="text: name"></span></p>
<p>Email: <span data-bind="text: email"></span></p>
</div>
<!-- Local variables -->
<div data-bind="let: { fullName: firstName() + ' ' + lastName() }">
<p data-bind="text: fullName"></p>
</div>Bindings for form elements and user interaction.
interface BindingHandlers {
/** Two-way binding for form field values */
value: {
init(element: HTMLElement, valueAccessor: () => any, allBindings: AllBindings): void;
update(element: HTMLElement, valueAccessor: () => any, allBindings: AllBindings): void;
after: string[];
};
/** Two-way binding for text input with immediate updates */
textInput: {
init(element: HTMLElement, valueAccessor: () => Observable<string>, allBindings: AllBindings): void;
};
/** Handle click events */
click: {
init(element: HTMLElement, valueAccessor: () => Function, allBindings: AllBindings, viewModel: any, bindingContext: BindingContext): void;
};
/** Handle multiple events */
event: {
init(element: HTMLElement, valueAccessor: () => object, allBindings: AllBindings, viewModel: any, bindingContext: BindingContext): void;
};
/** Handle form submission */
submit: {
init(element: HTMLElement, valueAccessor: () => Function, allBindings: AllBindings, viewModel: any, bindingContext: BindingContext): void;
};
/** Control element enabled state */
enable: {
update(element: HTMLElement, valueAccessor: () => any): void;
};
/** Control element disabled state */
disable: {
update(element: HTMLElement, valueAccessor: () => any): void;
};
/** Two-way binding for checkbox/radio checked state */
checked: {
init(element: HTMLElement, valueAccessor: () => any, allBindings: AllBindings): void;
after: string[];
};
/** Set value for checked radio button/checkbox */
checkedValue: {
update(element: HTMLElement, valueAccessor: () => any): void;
};
/** Two-way binding for focus state */
hasfocus: {
init(element: HTMLElement, valueAccessor: () => Observable<boolean>, allBindings: AllBindings): void;
update(element: HTMLElement, valueAccessor: () => Observable<boolean>): void;
};
/** Populate select element options */
options: {
init(element: HTMLElement): { controlsDescendantBindings: boolean };
update(element: HTMLElement, valueAccessor: () => any[], allBindings: AllBindings): void;
};
/** Two-way binding for multi-select selected options */
selectedOptions: {
init(element: HTMLElement, valueAccessor: () => Observable<any[]>, allBindings: AllBindings): void;
update(element: HTMLElement, valueAccessor: () => Observable<any[]>): void;
after: string[];
};
/** Generate unique name attribute */
uniqueName: {
init(element: HTMLElement, valueAccessor: () => boolean): void;
};
}Usage Examples:
<!-- Form field bindings -->
<input data-bind="value: userName" />
<input data-bind="textInput: searchTerm" />
<textarea data-bind="value: description"></textarea>
<!-- Event bindings -->
<button data-bind="click: handleClick">Click Me</button>
<button data-bind="event: { mouseover: onMouseOver, mouseout: onMouseOut }">Hover</button>
<form data-bind="submit: handleSubmit">
<input type="submit" value="Submit" />
</form>
<!-- Enable/disable -->
<button data-bind="enable: canSubmit">Submit</button>
<input data-bind="disable: isReadOnly" />
<!-- Checkboxes and radio buttons -->
<input type="checkbox" data-bind="checked: isSelected" />
<input type="radio" name="color" value="red" data-bind="checked: selectedColor, checkedValue: 'red'" />
<!-- Focus binding -->
<input data-bind="hasfocus: isSearchFocused" />
<!-- Select options -->
<select data-bind="options: availableCountries, value: selectedCountry, optionsText: 'name', optionsValue: 'id'"></select>
<!-- Multi-select -->
<select multiple data-bind="options: availableItems, selectedOptions: selectedItems"></select>Bindings for rendering templates and components.
interface BindingHandlers {
/** Render templates with data */
template: {
init(element: Node, valueAccessor: () => string | TemplateBindingOptions): { controlsDescendantBindings: boolean };
update(element: Node, valueAccessor: () => string | TemplateBindingOptions, allBindings: AllBindings, viewModel: any, bindingContext: BindingContext): void;
};
/** Render registered components */
component: {
init(element: Node, valueAccessor: () => { name: string; params?: any }, allBindings: AllBindings, viewModel: any, bindingContext: BindingContext): { controlsDescendantBindings: boolean };
};
}
interface TemplateBindingOptions {
name?: string | (() => string);
data?: any;
if?: boolean;
ifnot?: boolean;
foreach?: any[];
as?: string;
afterRender?: (elements: Node[], data: any) => void;
}Usage Examples:
<!-- Template binding -->
<div data-bind="template: 'user-template'"></div>
<div data-bind="template: { name: 'item-template', data: selectedItem }"></div>
<div data-bind="template: { name: 'list-template', foreach: items }"></div>
<!-- Component binding -->
<div data-bind="component: 'user-profile'"></div>
<div data-bind="component: { name: 'user-editor', params: { user: selectedUser } }"></div>
<!-- Template definitions -->
<script type="text/html" id="user-template">
<p>Name: <span data-bind="text: name"></span></p>
<p>Email: <span data-bind="text: email"></span></p>
</script>Creating custom binding handlers for specialized functionality.
/**
* Example of creating a custom binding handler
*/
ko.bindingHandlers.customBinding = {
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
// Initialize binding - called once when binding is first applied
const value = ko.utils.unwrapObservable(valueAccessor());
// Setup event handlers, initial state, etc.
// Optionally return { controlsDescendantBindings: true } to prevent
// descendant bindings from being applied automatically
},
update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
// Update binding - called whenever dependencies change
const value = ko.utils.unwrapObservable(valueAccessor());
// Update element based on new value
}
};Usage Example:
// Custom binding for setting element color
ko.bindingHandlers.color = {
update: function(element, valueAccessor) {
const color = ko.utils.unwrapObservable(valueAccessor());
element.style.color = color;
}
};
// Usage in HTML
// <span data-bind="color: textColor">Colored text</span>Virtual elements enable data binding on comment-based markup for cases where you cannot add HTML elements. They provide DOM manipulation functions that work with both regular elements and comment-based virtual elements.
/**
* Configuration for which bindings are allowed on virtual elements
*/
const virtualElements: {
allowedBindings: VirtualElementsAllowedBindings;
/** Get child nodes of an element or virtual element */
childNodes(node: Node): Node[];
/** Clear all child nodes from an element or virtual element */
emptyNode(node: Node): void;
/** Get first child of an element or virtual element */
firstChild(node: Node): Node;
/** Insert a node after another node in virtual element context */
insertAfter(node: Node, nodeToInsert: Node, insertAfterNode: Node): void;
/** Get next sibling of a node in virtual element context */
nextSibling(node: Node): Node;
/** Prepend a node to an element or virtual element */
prepend(node: Node, nodeToPrepend: Node): void;
/** Set child nodes of an element or virtual element */
setDomNodeChildren(node: Node, childNodes: Node[]): void;
};interface VirtualElementsAllowedBindings {
[bindingName: string]: boolean;
}Usage Examples:
<!-- Virtual element using comment syntax -->
<!-- ko if: showMessage -->
<p data-bind="text: message"></p>
<!-- /ko -->
<!-- Virtual foreach -->
<!-- ko foreach: items -->
<div data-bind="text: name"></div>
<!-- /ko -->
<!-- Virtual with binding -->
<!-- ko with: selectedItem -->
<div data-bind="text: title"></div>
<div data-bind="text: description"></div>
<!-- /ko -->import ko from "knockout";
// Allow custom binding on virtual elements
ko.virtualElements.allowedBindings.myCustomBinding = true;
// Use virtual element functions
const virtualStartComment = document.createComment("ko if: true");
const children = ko.virtualElements.childNodes(virtualStartComment);The binding provider system controls how Knockout discovers and processes data bindings in the DOM. This allows customization of binding syntax and preprocessing.
/**
* Default binding provider class for processing data-bind attributes
*/
class bindingProvider implements IBindingProvider {
/** Check if a node has bindings */
nodeHasBindings(node: Node): boolean;
/** Get binding object from node */
getBindings(node: Node, bindingContext: BindingContext): object;
/** Get binding accessor functions from node */
getBindingAccessors(node: Node, bindingContext: BindingContext): BindingAccessors;
/** Get raw binding string from node */
getBindingsString(node: Node, bindingContext?: BindingContext): string;
/** Parse binding string into binding object or accessors */
parseBindingsString(bindingsString: string, bindingContext: BindingContext, node: Node): object;
parseBindingsString(bindingsString: string, bindingContext: BindingContext, node: Node, options: BindingOptions): object | BindingAccessors;
/** Static reference to current binding provider instance */
static instance: IBindingProvider;
}interface IBindingProvider {
/** Check if node has bindings to process */
nodeHasBindings(node: Node): boolean;
/** Get bindings for a node (optional method) */
getBindings?(node: Node, bindingContext: BindingContext): object;
/** Get binding accessor functions for a node */
getBindingAccessors(node: Node, bindingContext: BindingContext): BindingAccessors;
/** Preprocess a node before binding (optional method) */
preprocessNode?(node: Node): Node[] | undefined;
}
interface BindingOptions {
/** Return accessor functions instead of values */
valueAccessors?: boolean;
/** Include binding parameters */
bindingParams?: boolean;
}Usage Examples:
import ko from "knockout";
// Custom binding provider
class MyBindingProvider {
nodeHasBindings(node) {
// Custom logic to detect bindings
return node.hasAttribute('my-bind');
}
getBindingAccessors(node, bindingContext) {
const bindingString = node.getAttribute('my-bind');
// Parse custom syntax and return accessors
return ko.bindingProvider.prototype.parseBindingsString(
bindingString, bindingContext, node
);
}
}
// Replace default binding provider
ko.bindingProvider.instance = new MyBindingProvider();
// Access current binding provider
const provider = ko.bindingProvider.instance;System for preprocessing and rewriting binding expressions before evaluation.
/**
* Expression rewriting utilities
*/
const expressionRewriting: {
/** Validators for binding rewrite operations */
bindingRewriteValidators: any[];
/** Parse object literal string into key-value pairs */
parseObjectLiteral(objectLiteralString: string): KeyValue[];
/** Preprocess binding string with options */
preProcessBindings(bindingsString: string, bindingOptions?: BindingOptions): string;
preProcessBindings(keyValueArray: KeyValue[], bindingOptions?: BindingOptions): string;
/** Internal two-way binding configuration */
_twoWayBindings: TwoWayBindings;
};interface KeyValue {
key?: string;
value?: string;
unknown?: string;
}
interface TwoWayBindings {
[bindingName: string]: boolean | string;
}Usage Examples:
import ko from "knockout";
// Parse object literal from binding string
const bindings = ko.expressionRewriting.parseObjectLiteral("text: name, click: save");
console.log(bindings); // [{ key: "text", value: "name" }, { key: "click", value: "save" }]
// Preprocess bindings with options
const processed = ko.expressionRewriting.preProcessBindings(
"value: userName",
{ valueAccessors: true }
);