0
# Data Binding System
1
2
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.
3
4
## Capabilities
5
6
### Binding Application
7
8
Functions to apply data bindings between view models and DOM elements.
9
10
```javascript { .api }
11
/**
12
* Apply bindings to connect view model to DOM
13
* @param viewModel - The view model object
14
* @param rootNode - Optional root DOM node (defaults to document.body)
15
*/
16
function applyBindings(viewModel: any, rootNode?: Node): void;
17
18
/**
19
* Apply bindings to descendant nodes only
20
* @param viewModel - The view model object
21
* @param rootNode - Root DOM node
22
*/
23
function applyBindingsToDescendants(viewModel: any, rootNode: Node): void;
24
25
/**
26
* Apply bindings to a specific DOM node
27
* @param node - Target DOM node
28
* @param bindings - Binding specifications object
29
* @param viewModel - The view model object
30
*/
31
function applyBindingsToNode(node: Node, bindings: object, viewModel: any): void;
32
33
/**
34
* Apply binding accessors to a specific DOM node
35
* @param node - Target DOM node
36
* @param bindingAccessors - Binding accessor functions
37
* @param viewModel - The view model object
38
*/
39
function applyBindingAccessorsToNode(node: Node, bindingAccessors: BindingAccessors, viewModel: any): void;
40
```
41
42
**Usage Examples:**
43
44
```javascript
45
import ko from "knockout";
46
47
// Basic binding application
48
function ViewModel() {
49
this.message = ko.observable("Hello World");
50
}
51
52
ko.applyBindings(new ViewModel());
53
54
// Binding to specific element
55
const element = document.getElementById("myDiv");
56
ko.applyBindings(new ViewModel(), element);
57
58
// Programmatic binding
59
const node = document.getElementById("target");
60
ko.applyBindingsToNode(node, {
61
text: "message",
62
click: "handleClick"
63
}, new ViewModel());
64
```
65
66
### Context Access
67
68
Functions to access binding context and data from DOM nodes.
69
70
```javascript { .api }
71
/**
72
* Get the binding context for a DOM node
73
* @param node - DOM node
74
* @returns Binding context or undefined
75
*/
76
function contextFor(node: Node): BindingContext | undefined;
77
78
/**
79
* Get the data object for a DOM node
80
* @param node - DOM node
81
* @returns Data object or undefined
82
*/
83
function dataFor(node: Node): any;
84
```
85
86
**Usage Examples:**
87
88
```javascript
89
import ko from "knockout";
90
91
const element = document.getElementById("bound-element");
92
const context = ko.contextFor(element);
93
const data = ko.dataFor(element);
94
95
console.log(context.$data); // View model data
96
console.log(data); // Same as context.$data
97
```
98
99
### Binding Context
100
101
Object that provides context information available in binding expressions.
102
103
```typescript { .api }
104
interface BindingContext<T = any> {
105
/** The current data object */
106
$data: T;
107
/** The root view model */
108
$root: any;
109
/** The parent data object */
110
$parent?: any;
111
/** Array of all parent data objects */
112
$parents: any[];
113
/** Observable index for items in foreach binding */
114
$index?: Observable<number>;
115
/** Reference to parent binding context */
116
$parentContext?: BindingContext<any>;
117
/** Current component instance (if inside component) */
118
$component?: any;
119
/** Reference to ko object */
120
ko: any;
121
122
/** Create an extended context with additional properties */
123
extend(properties: object): BindingContext<T>;
124
extend(properties: (self: BindingContext<T>) => object): BindingContext<T>;
125
126
/** Create a child context for nested data */
127
createChildContext<U>(
128
dataItem: U | Observable<U>,
129
dataItemAlias?: string,
130
extendCallback?: (context: BindingContext<U>) => void
131
): BindingContext<U>;
132
}
133
```
134
135
### Binding Handler Registry
136
137
Registry and access for binding handlers.
138
139
```javascript { .api }
140
/**
141
* Registry of all binding handlers
142
*/
143
const bindingHandlers: BindingHandlers;
144
145
/**
146
* Get a specific binding handler
147
* @param name - Binding handler name
148
* @returns Binding handler or undefined
149
*/
150
function getBindingHandler(name: string): BindingHandler | undefined;
151
```
152
153
```typescript { .api }
154
interface BindingHandler<T = any> {
155
/** Initialize the binding (called once) */
156
init?(
157
element: any,
158
valueAccessor: () => T,
159
allBindings: AllBindings,
160
viewModel: any,
161
bindingContext: BindingContext
162
): void | { controlsDescendantBindings: boolean };
163
164
/** Update the binding (called when dependencies change) */
165
update?(
166
element: any,
167
valueAccessor: () => T,
168
allBindings: AllBindings,
169
viewModel: any,
170
bindingContext: BindingContext
171
): void;
172
173
/** Array of binding names that must be processed after this one */
174
after?: string[];
175
176
/** Preprocess binding value before parsing */
177
preprocess?(
178
value: string | undefined,
179
name: string,
180
addBinding: (name: string, value: any) => void
181
): string | undefined | void;
182
}
183
184
interface AllBindings {
185
/** Get all bindings as object */
186
(): any;
187
/** Get specific binding value */
188
get(name: string): any;
189
get<T>(name: string): T;
190
/** Check if binding exists */
191
has(name: string): boolean;
192
}
193
194
interface BindingAccessors {
195
[name: string]: () => any;
196
}
197
```
198
199
### Text and Appearance Bindings
200
201
Bindings that control element text content, visibility, and styling.
202
203
```typescript { .api }
204
interface BindingHandlers {
205
/** Set element text content */
206
text: {
207
update(element: Node, valueAccessor: () => string): void;
208
};
209
210
/** Set element HTML content */
211
html: {
212
update(element: Node, valueAccessor: () => string): void;
213
};
214
215
/** Control element visibility */
216
visible: {
217
update(element: HTMLElement, valueAccessor: () => any): void;
218
};
219
220
/** Control element visibility (inverse of visible) */
221
hidden: {
222
update(element: HTMLElement, valueAccessor: () => any): void;
223
};
224
225
/** Set CSS class name */
226
class: {
227
update(element: HTMLElement, valueAccessor: () => string): void;
228
};
229
230
/** Set CSS classes based on object properties */
231
css: {
232
update(element: HTMLElement, valueAccessor: () => object | string): void;
233
};
234
235
/** Set inline styles */
236
style: {
237
update(element: HTMLElement, valueAccessor: () => object): void;
238
};
239
240
/** Set element attributes */
241
attr: {
242
update(element: HTMLElement, valueAccessor: () => object): void;
243
};
244
}
245
```
246
247
**Usage Examples:**
248
249
```html
250
<!-- Text and HTML content -->
251
<span data-bind="text: message"></span>
252
<div data-bind="html: htmlContent"></div>
253
254
<!-- Visibility -->
255
<div data-bind="visible: isVisible">Visible when true</div>
256
<div data-bind="hidden: isHidden">Hidden when true</div>
257
258
<!-- CSS classes and styles -->
259
<div data-bind="css: { active: isActive, disabled: !isEnabled }"></div>
260
<div data-bind="style: { color: textColor, fontSize: fontSize() + 'px' }"></div>
261
262
<!-- Attributes -->
263
<img data-bind="attr: { src: imageUrl, alt: imageDescription }" />
264
```
265
266
### Control Flow Bindings
267
268
Bindings that control conditional rendering and iteration.
269
270
```typescript { .api }
271
interface BindingHandlers {
272
/** Render template for each array item */
273
foreach: {
274
init(
275
element: Node,
276
valueAccessor: () => any[] | Observable<any[]>,
277
allBindings: AllBindings,
278
viewModel: any,
279
bindingContext: BindingContext
280
): { controlsDescendantBindings: boolean };
281
};
282
283
/** Conditionally include content */
284
if: {
285
init(
286
element: Node,
287
valueAccessor: () => any,
288
allBindings: AllBindings,
289
viewModel: any,
290
bindingContext: BindingContext
291
): { controlsDescendantBindings: boolean };
292
};
293
294
/** Conditionally exclude content */
295
ifnot: {
296
init(
297
element: Node,
298
valueAccessor: () => any,
299
allBindings: AllBindings,
300
viewModel: any,
301
bindingContext: BindingContext
302
): { controlsDescendantBindings: boolean };
303
};
304
305
/** Change binding context */
306
with: {
307
init(
308
element: Node,
309
valueAccessor: () => any,
310
allBindings: AllBindings,
311
viewModel: any,
312
bindingContext: BindingContext
313
): { controlsDescendantBindings: boolean };
314
};
315
316
/** Create local variable bindings */
317
let: {
318
init(
319
element: Node,
320
valueAccessor: () => object,
321
allBindings: AllBindings,
322
viewModel: any,
323
bindingContext: BindingContext
324
): { controlsDescendantBindings: boolean };
325
};
326
327
/** Alias for 'with' binding */
328
using: {
329
init(
330
element: Node,
331
valueAccessor: () => any,
332
allBindings: AllBindings,
333
viewModel: any,
334
bindingContext: BindingContext
335
): { controlsDescendantBindings: boolean };
336
};
337
}
338
```
339
340
**Usage Examples:**
341
342
```html
343
<!-- Foreach binding -->
344
<ul data-bind="foreach: items">
345
<li data-bind="text: $data"></li>
346
</ul>
347
348
<div data-bind="foreach: { data: people, as: 'person' }">
349
<p>Name: <span data-bind="text: person.name"></span></p>
350
<p>Index: <span data-bind="text: $index"></span></p>
351
</div>
352
353
<!-- Conditional bindings -->
354
<div data-bind="if: showDetails">
355
<p>Details content here</p>
356
</div>
357
358
<div data-bind="ifnot: isLoading">
359
<p>Content loaded</p>
360
</div>
361
362
<!-- Context binding -->
363
<div data-bind="with: selectedUser">
364
<p>Name: <span data-bind="text: name"></span></p>
365
<p>Email: <span data-bind="text: email"></span></p>
366
</div>
367
368
<!-- Local variables -->
369
<div data-bind="let: { fullName: firstName() + ' ' + lastName() }">
370
<p data-bind="text: fullName"></p>
371
</div>
372
```
373
374
### Form Field Bindings
375
376
Bindings for form elements and user interaction.
377
378
```typescript { .api }
379
interface BindingHandlers {
380
/** Two-way binding for form field values */
381
value: {
382
init(element: HTMLElement, valueAccessor: () => any, allBindings: AllBindings): void;
383
update(element: HTMLElement, valueAccessor: () => any, allBindings: AllBindings): void;
384
after: string[];
385
};
386
387
/** Two-way binding for text input with immediate updates */
388
textInput: {
389
init(element: HTMLElement, valueAccessor: () => Observable<string>, allBindings: AllBindings): void;
390
};
391
392
/** Handle click events */
393
click: {
394
init(element: HTMLElement, valueAccessor: () => Function, allBindings: AllBindings, viewModel: any, bindingContext: BindingContext): void;
395
};
396
397
/** Handle multiple events */
398
event: {
399
init(element: HTMLElement, valueAccessor: () => object, allBindings: AllBindings, viewModel: any, bindingContext: BindingContext): void;
400
};
401
402
/** Handle form submission */
403
submit: {
404
init(element: HTMLElement, valueAccessor: () => Function, allBindings: AllBindings, viewModel: any, bindingContext: BindingContext): void;
405
};
406
407
/** Control element enabled state */
408
enable: {
409
update(element: HTMLElement, valueAccessor: () => any): void;
410
};
411
412
/** Control element disabled state */
413
disable: {
414
update(element: HTMLElement, valueAccessor: () => any): void;
415
};
416
417
/** Two-way binding for checkbox/radio checked state */
418
checked: {
419
init(element: HTMLElement, valueAccessor: () => any, allBindings: AllBindings): void;
420
after: string[];
421
};
422
423
/** Set value for checked radio button/checkbox */
424
checkedValue: {
425
update(element: HTMLElement, valueAccessor: () => any): void;
426
};
427
428
/** Two-way binding for focus state */
429
hasfocus: {
430
init(element: HTMLElement, valueAccessor: () => Observable<boolean>, allBindings: AllBindings): void;
431
update(element: HTMLElement, valueAccessor: () => Observable<boolean>): void;
432
};
433
434
/** Populate select element options */
435
options: {
436
init(element: HTMLElement): { controlsDescendantBindings: boolean };
437
update(element: HTMLElement, valueAccessor: () => any[], allBindings: AllBindings): void;
438
};
439
440
/** Two-way binding for multi-select selected options */
441
selectedOptions: {
442
init(element: HTMLElement, valueAccessor: () => Observable<any[]>, allBindings: AllBindings): void;
443
update(element: HTMLElement, valueAccessor: () => Observable<any[]>): void;
444
after: string[];
445
};
446
447
/** Generate unique name attribute */
448
uniqueName: {
449
init(element: HTMLElement, valueAccessor: () => boolean): void;
450
};
451
}
452
```
453
454
**Usage Examples:**
455
456
```html
457
<!-- Form field bindings -->
458
<input data-bind="value: userName" />
459
<input data-bind="textInput: searchTerm" />
460
<textarea data-bind="value: description"></textarea>
461
462
<!-- Event bindings -->
463
<button data-bind="click: handleClick">Click Me</button>
464
<button data-bind="event: { mouseover: onMouseOver, mouseout: onMouseOut }">Hover</button>
465
<form data-bind="submit: handleSubmit">
466
<input type="submit" value="Submit" />
467
</form>
468
469
<!-- Enable/disable -->
470
<button data-bind="enable: canSubmit">Submit</button>
471
<input data-bind="disable: isReadOnly" />
472
473
<!-- Checkboxes and radio buttons -->
474
<input type="checkbox" data-bind="checked: isSelected" />
475
<input type="radio" name="color" value="red" data-bind="checked: selectedColor, checkedValue: 'red'" />
476
477
<!-- Focus binding -->
478
<input data-bind="hasfocus: isSearchFocused" />
479
480
<!-- Select options -->
481
<select data-bind="options: availableCountries, value: selectedCountry, optionsText: 'name', optionsValue: 'id'"></select>
482
483
<!-- Multi-select -->
484
<select multiple data-bind="options: availableItems, selectedOptions: selectedItems"></select>
485
```
486
487
### Template and Component Bindings
488
489
Bindings for rendering templates and components.
490
491
```typescript { .api }
492
interface BindingHandlers {
493
/** Render templates with data */
494
template: {
495
init(element: Node, valueAccessor: () => string | TemplateBindingOptions): { controlsDescendantBindings: boolean };
496
update(element: Node, valueAccessor: () => string | TemplateBindingOptions, allBindings: AllBindings, viewModel: any, bindingContext: BindingContext): void;
497
};
498
499
/** Render registered components */
500
component: {
501
init(element: Node, valueAccessor: () => { name: string; params?: any }, allBindings: AllBindings, viewModel: any, bindingContext: BindingContext): { controlsDescendantBindings: boolean };
502
};
503
}
504
505
interface TemplateBindingOptions {
506
name?: string | (() => string);
507
data?: any;
508
if?: boolean;
509
ifnot?: boolean;
510
foreach?: any[];
511
as?: string;
512
afterRender?: (elements: Node[], data: any) => void;
513
}
514
```
515
516
**Usage Examples:**
517
518
```html
519
<!-- Template binding -->
520
<div data-bind="template: 'user-template'"></div>
521
<div data-bind="template: { name: 'item-template', data: selectedItem }"></div>
522
<div data-bind="template: { name: 'list-template', foreach: items }"></div>
523
524
<!-- Component binding -->
525
<div data-bind="component: 'user-profile'"></div>
526
<div data-bind="component: { name: 'user-editor', params: { user: selectedUser } }"></div>
527
528
<!-- Template definitions -->
529
<script type="text/html" id="user-template">
530
<p>Name: <span data-bind="text: name"></span></p>
531
<p>Email: <span data-bind="text: email"></span></p>
532
</script>
533
```
534
535
### Custom Binding Creation
536
537
Creating custom binding handlers for specialized functionality.
538
539
```javascript { .api }
540
/**
541
* Example of creating a custom binding handler
542
*/
543
ko.bindingHandlers.customBinding = {
544
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
545
// Initialize binding - called once when binding is first applied
546
const value = ko.utils.unwrapObservable(valueAccessor());
547
// Setup event handlers, initial state, etc.
548
549
// Optionally return { controlsDescendantBindings: true } to prevent
550
// descendant bindings from being applied automatically
551
},
552
553
update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
554
// Update binding - called whenever dependencies change
555
const value = ko.utils.unwrapObservable(valueAccessor());
556
// Update element based on new value
557
}
558
};
559
```
560
561
**Usage Example:**
562
563
```javascript
564
// Custom binding for setting element color
565
ko.bindingHandlers.color = {
566
update: function(element, valueAccessor) {
567
const color = ko.utils.unwrapObservable(valueAccessor());
568
element.style.color = color;
569
}
570
};
571
572
// Usage in HTML
573
// <span data-bind="color: textColor">Colored text</span>
574
```
575
576
### Virtual Elements
577
578
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.
579
580
```javascript { .api }
581
/**
582
* Configuration for which bindings are allowed on virtual elements
583
*/
584
const virtualElements: {
585
allowedBindings: VirtualElementsAllowedBindings;
586
587
/** Get child nodes of an element or virtual element */
588
childNodes(node: Node): Node[];
589
590
/** Clear all child nodes from an element or virtual element */
591
emptyNode(node: Node): void;
592
593
/** Get first child of an element or virtual element */
594
firstChild(node: Node): Node;
595
596
/** Insert a node after another node in virtual element context */
597
insertAfter(node: Node, nodeToInsert: Node, insertAfterNode: Node): void;
598
599
/** Get next sibling of a node in virtual element context */
600
nextSibling(node: Node): Node;
601
602
/** Prepend a node to an element or virtual element */
603
prepend(node: Node, nodeToPrepend: Node): void;
604
605
/** Set child nodes of an element or virtual element */
606
setDomNodeChildren(node: Node, childNodes: Node[]): void;
607
};
608
```
609
610
```typescript { .api }
611
interface VirtualElementsAllowedBindings {
612
[bindingName: string]: boolean;
613
}
614
```
615
616
**Usage Examples:**
617
618
```html
619
<!-- Virtual element using comment syntax -->
620
<!-- ko if: showMessage -->
621
<p data-bind="text: message"></p>
622
<!-- /ko -->
623
624
<!-- Virtual foreach -->
625
<!-- ko foreach: items -->
626
<div data-bind="text: name"></div>
627
<!-- /ko -->
628
629
<!-- Virtual with binding -->
630
<!-- ko with: selectedItem -->
631
<div data-bind="text: title"></div>
632
<div data-bind="text: description"></div>
633
<!-- /ko -->
634
```
635
636
```javascript
637
import ko from "knockout";
638
639
// Allow custom binding on virtual elements
640
ko.virtualElements.allowedBindings.myCustomBinding = true;
641
642
// Use virtual element functions
643
const virtualStartComment = document.createComment("ko if: true");
644
const children = ko.virtualElements.childNodes(virtualStartComment);
645
```
646
647
### Binding Provider
648
649
The binding provider system controls how Knockout discovers and processes data bindings in the DOM. This allows customization of binding syntax and preprocessing.
650
651
```javascript { .api }
652
/**
653
* Default binding provider class for processing data-bind attributes
654
*/
655
class bindingProvider implements IBindingProvider {
656
/** Check if a node has bindings */
657
nodeHasBindings(node: Node): boolean;
658
659
/** Get binding object from node */
660
getBindings(node: Node, bindingContext: BindingContext): object;
661
662
/** Get binding accessor functions from node */
663
getBindingAccessors(node: Node, bindingContext: BindingContext): BindingAccessors;
664
665
/** Get raw binding string from node */
666
getBindingsString(node: Node, bindingContext?: BindingContext): string;
667
668
/** Parse binding string into binding object or accessors */
669
parseBindingsString(bindingsString: string, bindingContext: BindingContext, node: Node): object;
670
parseBindingsString(bindingsString: string, bindingContext: BindingContext, node: Node, options: BindingOptions): object | BindingAccessors;
671
672
/** Static reference to current binding provider instance */
673
static instance: IBindingProvider;
674
}
675
```
676
677
```typescript { .api }
678
interface IBindingProvider {
679
/** Check if node has bindings to process */
680
nodeHasBindings(node: Node): boolean;
681
682
/** Get bindings for a node (optional method) */
683
getBindings?(node: Node, bindingContext: BindingContext): object;
684
685
/** Get binding accessor functions for a node */
686
getBindingAccessors(node: Node, bindingContext: BindingContext): BindingAccessors;
687
688
/** Preprocess a node before binding (optional method) */
689
preprocessNode?(node: Node): Node[] | undefined;
690
}
691
692
interface BindingOptions {
693
/** Return accessor functions instead of values */
694
valueAccessors?: boolean;
695
/** Include binding parameters */
696
bindingParams?: boolean;
697
}
698
```
699
700
**Usage Examples:**
701
702
```javascript
703
import ko from "knockout";
704
705
// Custom binding provider
706
class MyBindingProvider {
707
nodeHasBindings(node) {
708
// Custom logic to detect bindings
709
return node.hasAttribute('my-bind');
710
}
711
712
getBindingAccessors(node, bindingContext) {
713
const bindingString = node.getAttribute('my-bind');
714
// Parse custom syntax and return accessors
715
return ko.bindingProvider.prototype.parseBindingsString(
716
bindingString, bindingContext, node
717
);
718
}
719
}
720
721
// Replace default binding provider
722
ko.bindingProvider.instance = new MyBindingProvider();
723
724
// Access current binding provider
725
const provider = ko.bindingProvider.instance;
726
```
727
728
### Expression Rewriting
729
730
System for preprocessing and rewriting binding expressions before evaluation.
731
732
```javascript { .api }
733
/**
734
* Expression rewriting utilities
735
*/
736
const expressionRewriting: {
737
/** Validators for binding rewrite operations */
738
bindingRewriteValidators: any[];
739
740
/** Parse object literal string into key-value pairs */
741
parseObjectLiteral(objectLiteralString: string): KeyValue[];
742
743
/** Preprocess binding string with options */
744
preProcessBindings(bindingsString: string, bindingOptions?: BindingOptions): string;
745
preProcessBindings(keyValueArray: KeyValue[], bindingOptions?: BindingOptions): string;
746
747
/** Internal two-way binding configuration */
748
_twoWayBindings: TwoWayBindings;
749
};
750
```
751
752
```typescript { .api }
753
interface KeyValue {
754
key?: string;
755
value?: string;
756
unknown?: string;
757
}
758
759
interface TwoWayBindings {
760
[bindingName: string]: boolean | string;
761
}
762
```
763
764
**Usage Examples:**
765
766
```javascript
767
import ko from "knockout";
768
769
// Parse object literal from binding string
770
const bindings = ko.expressionRewriting.parseObjectLiteral("text: name, click: save");
771
console.log(bindings); // [{ key: "text", value: "name" }, { key: "click", value: "save" }]
772
773
// Preprocess bindings with options
774
const processed = ko.expressionRewriting.preProcessBindings(
775
"value: userName",
776
{ valueAccessors: true }
777
);
778
```