'Loading' Placeholder using Knockout
An example of a custom knockout binding that can replace the content of an element with a loading marker.
by stevegreatrex
HTML
<script src="http://knockoutjs.com/downloads/knockout-3.0.0.js"></script>
<div data-bind="loadingWhen: isLoading" class="content">
<span>This static content will be hidden whilst loading</span>
</div>
<a href="#" data-bind="click: asyncOperation">Fake A Load</a>
CSS
body { font-family: 'Segoe UI', Verdana; padding: 10px; }
.content { height: 100px; width: 400px; background: #aaa; padding: 5px; }
.loader { background-image: url(data:image/gif;base64,R0lGODlhEAALAPQAAP///0pISOTk5N3d3e/v705MTEpISGpoaKalpY6MjM3NzWJgYH59fauqqpCPj9DQ0GRjY0xKSoF/f+zs7OPi4vb29nFwcOXl5fX19crKyru6utnY2PLy8gAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCwAAACwAAAAAEAALAAAFLSAgjmRpnqSgCuLKAq5AEIM4zDVw03ve27ifDgfkEYe04kDIDC5zrtYKRa2WQgAh+QQJCwAAACwAAAAAEAALAAAFJGBhGAVgnqhpHIeRvsDawqns0qeN5+y967tYLyicBYE7EYkYAgAh+QQJCwAAACwAAAAAEAALAAAFNiAgjothLOOIJAkiGgxjpGKiKMkbz7SN6zIawJcDwIK9W/HISxGBzdHTuBNOmcJVCyoUlk7CEAAh+QQJCwAAACwAAAAAEAALAAAFNSAgjqQIRRFUAo3jNGIkSdHqPI8Tz3V55zuaDacDyIQ+YrBH+hWPzJFzOQQaeavWi7oqnVIhACH5BAkLAAAALAAAAAAQAAsAAAUyICCOZGme1rJY5kRRk7hI0mJSVUXJtF3iOl7tltsBZsNfUegjAY3I5sgFY55KqdX1GgIAIfkECQsAAAAsAAAAABAACwAABTcgII5kaZ4kcV2EqLJipmnZhWGXaOOitm2aXQ4g7P2Ct2ER4AMul00kj5g0Al8tADY2y6C+4FIIACH5BAkLAAAALAAAAAAQAAsAAAUvICCOZGme5ERRk6iy7qpyHCVStA3gNa/7txxwlwv2isSacYUc+l4tADQGQ1mvpBAAIfkECQsAAAAsAAAAABAACwAABS8gII5kaZ7kRFGTqLLuqnIcJVK0DeA1r/u3HHCXC/aKxJpxhRz6Xi0ANAZDWa+kEAA7AAAAAAAAAAAA); height: 11px; width: 16px; }
JavaScript
//
// Custom Binding
//
ko.bindingHandlers.loadingWhen = {
init: function (element) {
var
$element = $(element),
currentPosition = $element.css("position")
$loader = $("<div>").addClass("loader").hide();
//add the loader
$element.append($loader);
//make sure that we can absolutely position the loader against the original element
if (currentPosition == "auto" || currentPosition == "static")
$element.css("position", "relative");
//center the loader
$loader.css({
position: "absolute",
top: "50%",
left: "50%",
"margin-left": -($loader.width() / 2) + "px",
"margin-top": -($loader.height() / 2) + "px"
});
},
update: function (element, valueAccessor) {
var isLoading = ko.utils.unwrapObservable(valueAccessor()),
$element = $(element),
$childrenToHide = $element.children(":not(div.loader)"),
$loader = $element.find("div.loader");
if (isLoading) {
$childrenToHide.css("visibility", "hidden").attr("disabled", "disabled");
$loader.show();
}
else {
$loader.fadeOut("fast");
$childrenToHide.css("visibility", "visible").removeAttr("disabled");
}
}
};
//
// Sample View Model
//
var ViewModel = function() {
var _self = this;
this.isLoading = ko.observable(false);
this.asyncOperation = function() {
_self.isLoading(true);
setTimeout(function() {
//wait a few seconds and then just return
_self.isLoading(false);
}, 3000);
};
};
ko.applyBindings(new ViewModel());