Edit in JSFiddle

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 context
                    if (typeof options.callback === "string" && typeof context[options.callback] === "function") {
                        return context[options.callback].call(context, event);
                    }
                    //if a function was passed, then give it the real context as a param
                    return options.callback.call(viewModel, context, event);
                }
            }

            var realValueAccessor = function() {
                var result = {};
                result[eventOptions.event] = realCallback;
                return result;
            }

            ko.bindingHandlers.event.init(element, realValueAccessor, allBindings, viewModel);
        });
    }
};

//binding to do event delegation for click events
ko.bindingHandlers.delegatedClick = {
    init: function(element, valueAccessor, allBindings, viewModel) {
        var clicksToHandle = valueAccessor();
        //if a single event was passed, then convert it to an array
        if (!$.isArray(clicksToHandle)) {
            clicksToHandle = [clicksHandle];
        }
        ko.utils.arrayMap(clicksToHandle, function(options) {
            options.event = 'click';
            return options;
        });

        var realValueAccessor = function() {
            return clicksToHandle;
        }

        ko.bindingHandlers.delegatedEvent.init(element, realValueAccessor, allBindings, viewModel)
    }
}
    
//binding to do event delegation for click events
ko.bindingHandlers.delegatedClickLong = {
    init: function(element, valueAccessor, allBindings, viewModel) {
        var clicksToHandle = valueAccessor();
        //if a single event was passed, then convert it to an array
        if (!$.isArray(clicksToHandle)) {
            clicksToHandle = [clicksHandle];
        }
        ko.utils.arrayForEach(clicksToHandle, function(options) {
            //build a function that will call our function with the correct context
            var realValueAccessor = function() {
                return function(event) {
                    var element = event.target;
                    //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 context
                        if (typeof options.callback === "string" && typeof context[options.callback] === "function") {
                            return context[options.callback].call(context, event);
                        }
                        //if a function was passed, then give it the real context as a param
                        return options.callback.call(viewModel, context, event);
                    }
                }
            }

            //call real click binding's init    
            ko.bindingHandlers.click.init(element, realValueAccessor, allBindings, viewModel);
        });
    }
}

ko.applyBindings(viewModel);











<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>
div { height: 20px; }
table { border: black solid 1px; }
td, th { padding: 1px; text-align: center; }