JSFiddle - React, Tailwind, and code Playground
by badsyntax
HTML
<ul id="list" data-bind="foreach: items, click: onListClick">
<li>
<a href="#">
<!-- ko text: name --><!-- /ko -->
<span class="actions">
<span class="add" title="Add">+</span>
<span class="remove" title="Remove">X</span>
</span>
</a>
</li>
</ul>
<!-- Testing closest() which checks ancestors -->
<div class="foo"></div>
CSS
#list {
border: 1px solid #eee;
width: 200px;
margin: 1em;
}
#list li {
overflow: hidden;
}
#list a {
display: block;
}
#list a:hover {
background: #ddd;
}
#list .actions {
float: right;
}
#list .actions span {
display: inline-block;
width: 20px;
border: 1px solid #ddd;
text-align: center;
}
#list .actions span:hover {
background: blue;
color: white;
}
JavaScript
(function() {
// Knockout event delegation idea, by Richard Willis
// NOTE: not supported in < IE8
var delegateEvent = (function() {
// You could refactor this to use $.fn.closest() to have
// better browser support.
function matchesSelector(selector, e) {
var all = e.currentTarget.querySelectorAll(selector);
var l = all.length;
var i = 0;
for (i; i < l; i++) {
if (all[i] === e.target) {
return true;
}
}
return false;
}
return function(e, handlers) {
for(var key in handlers) {
if (matchesSelector(key, e)) {
handlers[key](ko.dataFor(e.target), e);
break; // only match the first selector
}
}
};
}());
var ListViewModel = function () {
this.items = ko.observableArray([{
name: 'List item 1'
}, {
name: 'List item 2'
}, {
name: 'List item 3'
}, {
name: 'List item 4'
}, {
name: 'List item 5'
}, {
name: 'List item 6'
}]);
this.onListClick = function(vm, e) {
e.preventDefault();
// Delegate the click event to the appropriate handler
delegateEvent(e, {
'a .add': this.onAddClick.bind(this),
'a .remove': this.onRemoveClick.bind(this),
'a': this.onAnchorClick.bind(this)
});
}
this.onAnchorClick = function(model, e) {
alert('Clicked on ANCHOR for: ' + model.name);
};
this.onAddClick = function(model, e) {
alert('Clicked on ADD for: ' + model.name);
};
this.onRemoveClick = function(model, e) {
alert('Clicked on REMOVE for: ' + model.name);
};
};
ko.applyBindings(new ListViewModel(), document.getElementById('list'));
}());