I had a similar problem. In my case, I needed to hide a whole block of HTML if an image inside the block could not be loaded. I ended up using the imagesLoaded library (https://github.com/desandro/imagesloaded), which I wrapped in a knockout custom binding :
function tryRegisterEvent(imgLoad, event, handler) {
if (handler === undefined) return;
imgLoad.on(event, handler);
}
function tryRegisterEvents(imgLoad, events, bindings) {
for (var i = 0; i < events.length; ++i) {
var event = events[i];
tryRegisterEvent(imgLoad, event, bindings[event]);
}
}
ko.bindingHandlers['imagesLoaded'] = {
'init': function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
if (imagesLoaded === undefined) {
throw new Error('imagesLoaded is not defined');
}
var bindings = ko.utils.unwrapObservable(valueAccessor());
var imgLoad = imagesLoaded(element);
tryRegisterEvents(imgLoad, ['always', 'done', 'fail', 'progress'], bindings);
},
'update': function () {}
};
Then I could use this binding in my HTML, as follow:
<div data-bind="visible: isLoading() || isLoaded()">
Some more HTML and text...
<img src="..." data-bind="imagesLoaded: { done: function () { isLoaded(true); }, always: function () { isLoading(false); } }" />
</div>
I initially set isLoading
to true
and isLoaded
to false
, and the event handlers would then change my view model's state accordingly, based on the image load status.
Note that since the imagesLoaded library can work with container instead of single images (and monitor all images inside the container), you can use this custom binding on a parent element containing all your image gallery, then display your spinner and hide it when the always
event is triggered.