Event Delegation in KnockoutJS
http://www.knockmeout.net/2011/04/event-delegation-in-knockoutjs.html
by rniemeyer
HTML
<script src="http://rniemeyer.github.com/KnockMeOut/Scripts/jquery.tmpl.js"></script>
<script src="http://knockoutjs.com/downloads/knockout-2.2.1.debug.js"></script>
<div data-bind="text: description">
</div>
<table data-bind="template: 'rowsTmpl', delegatedClick: [{callback: 'toggleShowDetails', selector: 'td a' }, { callback: deleteRow, selector: 'button' }]"></table>
<script id="rowsTmpl" type="text/html">
<thead data-bind="delegatedEvent: [{ event: 'mouseover', callback: setDescription, selector: 'th'}, { event: 'mouseout', callback: clearDescription, selector: 'th'}]">
<tr data-bind="template: { name: 'headTmpl', foreach: headers }"><th></th></tr>
</thead>
<tbody data-bind="template: { name: 'rowTmpl', foreach: rows }"></tbody>
</script>
<script id="rowTmpl" type="text/html">
<tr data-bind="template: { name: 'cellTmpl', foreach: cells }">
<td><button>Delete</button></td>
</tr>
</script>
<script id="headTmpl" type="text/html">
<th data-bind="text: name"></th>
</script>
<script id="cellTmpl" type="text/html">
<td>
<a href="javascript: void(0);" data-bind="text: name"></a>
<div data-bind="visible: showDetails, text: $data"></div>
</td>
</script>
CSS
div { height: 20px; }
table { border: black solid 1px; }
td, th { padding: 1px; text-align: center; }
JavaScript
function Cell(name, x, y) {
this.name = name;
this.x = x;
this.y = y;
this.showDetails = ko.observable(false);
this.toggleShowDetails = function() {
this.showDetails(!this.showDetails());
}.bind(this);
}
Cell.prototype.toString = function() {
return "(" + this.x + "," + this.y + ")";
}
var viewModel = {
counter: 0,
headers: ko.observableArray([]),
rows: ko.observableArray([]),
deleteRow: function(row) {
viewModel.rows.remove(row);
},
setDescription: function(header) {
viewModel.description(header.description);
},
clearDescription: function() {
viewModel.description("");
},
description: ko.observable("")
};
//setup a 20x20 grid of cells
for (var i = 0; i < 15; i++) {
var row = {
name: i,
cells: []
};
for (var j = 0; j < 15; j++) {
row.cells.push(new Cell(viewModel.counter++, i, j));
}
viewModel.rows.push(row);
viewModel.headers.push({
name: i,
description: "This is a description for column " + i
});
}
//binding to do event delegation for any event
ko.bindingHandlers.delegatedEvent = {
init: function(element, valueAccessor, allBindings, viewModel) {
var eventsToHandle = valueAccessor() || {};
//if a single event was passed, then convert it to an array
if (!$.isArray(eventsToHandle)) {
eventsToHandle = [eventsToHandle];
}
ko.utils.arrayForEach(eventsToHandle, function(eventOptions) {
var realCallback = function(event) {
var element = event.target;
var options = eventOptions;
//verify that the element matches our selector
if ($(element).is(options.selector)) {
//get real context
var context = $(event.target).tmplItem().data;
//if a string was passed for the function, then assume it is a function of the real...