Isotope.js with Knockout.js
isotope works fine at first being able to sort and remove items just fine. But the second new items are added then isotope falls apart.
by evanlarsen
HTML
<script src="https://github.com/SteveSanderson/knockout/raw/master/build/output/knockout-latest.debug.js"></script>
<script src="https://github.com/desandro/isotope/raw/master/jquery.isotope.js"></script>
<div class="root">
<div id="new-service-container">
<table>
<tr>
<td>Name: </td>
<td><input type="text" data-bind="value: newName" /></td>
<td>ID: </td><td><input type="text" data-bind="value: newID"/></td>
</tr>
<tr>
<td>Description: </td>
<td><input type="text" data-bind="value: newDescription" /></td>
</tr>
<tr>
<td colspan="2"><input type="button" value="Add service" data-bind="click: addService" /></td>
</tr>
</table>
<div id="sort-actions">
<span>sort by:</span>
<a data-bind="click: sortByName">Name</a>
<a data-bind="click: sortByDescription">Description</a>
<a data-bind="click: sortByID">ID</a>
<a data-bind="click: sortByDate">Date</a>
<br />
<span>sort direction</span>
<a data-bind="click: sortAsc">Ascending</a>
<a data-bind="click: sortDesc">Descending</a>
</div>
</div>
<ul id="container" data-bind="template: { name: 'services-template', foreach: services, afterAdd: serviceAdded, beforeRemove: serviceRemoved }, jqIsotope: isotope">
</ul>
</div>
<script type="text/html" id="services-template">
<li class="element" data-bind="click: $parent.removeService">
<div class="id" data-bind="text: id"></div>
<div class="name" data-bind="text: name"></div>
<div class="description" data-bind="text: description"></div>
<div class="date" data-bind="text: date"></div>
</li>
</script>
CSS
#root { overflow: hidden; }
#container { list-style-type: none; overflow: hidden; }
#container >li { display: block; padding: 12px; margin: 8px; border: 2px outset #eee; border-radius: 4px; float: left; overflow: hidden; cursor: pointer; }
#container >li:hover { background-color: #eee; }
#container >li >div { font-size: 24px; }
a { color: blue; display: inline-block; padding: 2px 4px; cursor: pointer; }
a:hover { text-decoration: underline; }
.isotope-item {
z-index: 2;
}
.isotope-hidden.isotope-item {
pointer-events: none;
z-index: 1;
}
/**** Isotope CSS3 transitions ****/
.isotope,
.isotope .isotope-item {
-webkit-transition-duration: 0.8s;
-moz-transition-duration: 0.8s;
-ms-transition-duration: 0.8s;
-o-transition-duration: 0.8s;
transition-duration: 0.8s;
}
.isotope {
-webkit-transition-property: height, width;
-moz-transition-property: height, width;
-ms-transition-property: height, width;
-o-transition-property: height, width;
transition-property: height, width;
}
.isotope .isotope-item {
-webkit-transition-property: -webkit-transform, opacity;
-moz-transition-property: -moz-transform, opacity;
-ms-transition-property: -ms-transform, opacity;
-o-transition-property: top, left, opacity;
transition-property: transform, opacity;
}
/**** disabling Isotope CSS3 transitions ****/
.isotope.no-transition,
.isotope.no-transition .isotope-item,
.isotope .isotope-item.no-transition {
-webkit-transition-duration: 0s;
-moz-transition-duration: 0s;
-ms-transition-duration: 0s;
-o-transition-duration: 0s;
transition-duration: 0s;
}
JavaScript
ko.bindingHandlers['jqIsotope'] = {
'update': function (element, valueAccessor, allBindingsAccessor, viewModel) {
var options = ko.utils.unwrapObservable(valueAccessor());
if (options) {
if (Object.prototype.toString.call(options) === '[object Array]') {
$.fn.isotope.apply($(element), options);
} else {
$(element).isotope(options);
}
}
}
};
$(function() {
function Service(name, description, id, date) {
var self = this;
self.name = ko.observable(name);
self.description = ko.observable(description);
self.id = ko.observable(id);
self.date = ko.observable(date);
}
function ViewModel() {
var self = this;
self.newName = ko.observable();
self.newDescription = ko.observable();
self.newID = ko.observable();
self.services = ko.observableArray([]);
self.isotope = ko.observable();
self.addService = function() {
self.services.push(new Service(self.newName(), self.newDescription(), self.newID()));
};
self.removeService = function(service) {
self.services.remove(service);
};
self.sortByName = function() {
self.isotope({
sortBy: 'name'
});
};
self.sortByDescription = function() {
self.isotope({
sortBy: 'description'
});
};
self.sortByID = function() {
$('#container').isotope({
sortBy: 'id'
});
};
self.sortByDate = function() {
self.isotope({
sortBy: 'date'
});
};
self.sortAsc = function() {
self.isotope({
sortAscending: true
});
};
self.sortDesc = function() {
self.isotope({
sortAscending: false
});
...