I have this piece of code.
<img data-bind="attr: {src: 'imagePath'}, style: { 'background-image': 'url('imagePath')' }" class="img-responsive">
The problem is it is showing two images. One is the image ing from src
and other one ing from background
image. My goal was to enable the background
image when the src
image is not available.
I have this piece of code.
<img data-bind="attr: {src: 'imagePath'}, style: { 'background-image': 'url('imagePath')' }" class="img-responsive">
The problem is it is showing two images. One is the image ing from src
and other one ing from background
image. My goal was to enable the background
image when the src
image is not available.
-
Why don't you only use the
src
attribute, and wire it to an observable that will resolve the image source when it bees available? – cl3m Commented Feb 15, 2016 at 20:25 - Default to showing the fallback image, and replace it if and when the other image is found. – Gary Hayes Commented Feb 15, 2016 at 20:45
- Using a background image as the alternate will create some problems, e.g., it will still display a broken image icon and you will need to set the height and width. That is why using only the src seems a better solution. – Yogi Commented Feb 15, 2016 at 21:02
- Just wondering if my answer did, in fact, answer your question? – JDR Commented Feb 20, 2016 at 23:24
3 Answers
Reset to default 9What you can do is create a custom binding, let's call it safeSrc
.
In this binding, you listen to the load
and error
events of your image - rendering your image if it's loaded successfully and rendering a fallback if it is not.
In practice, it could look like the following:
ko.bindingHandlers.safeSrc = {
update: function(element, valueAccessor) {
var options = valueAccessor();
var src = ko.unwrap(options.src);
$('<img />').attr('src', src).on('load', function() {
$(element).attr('src', src);
}).on('error', function() {
$(element).attr('src', ko.unwrap(options.fallback));
});
}
};
And then, you can apply the binding like this:
<img alt="Foo" data-bind="safeSrc: {src: imageObservable, fallback: 'https://example./fallback.png'}" />
Note that this presumes you're using jQuery - but you can easily rewrite the event listener.
Finally, I would also like to say that you should be rendering a different src
instead of the background image - unless you have a specific reason to require one?
Either way, you can simply change the line $(element).attr('src', ko.unwrap(options.fallback));
to $(element).css('background-image', 'url(' + ko.unwrap(options.fallback) + ')');
.
JS Fiddle Demo
Here, you can see it all in action: https://jsfiddle/13vkutkv/2/
(EDIT: I replaced the cheeky hotlink to the Knockout JS logo with Placehold.it)
P.S.: Depending on how you wish to interact with the element in future (interacting with/updating the binding), you may wish to use applyBindingsToNode
(i.e. the Knockout-way), rather than manipulating the src
attribute directly on the DOM element.
To show alternate image if img src is not found make alternate image link in your server logic and use only src: 'imagePath' in your front-end
Or if it is important to do it in front-end, you should look at this post: Display alternate image
I always check my images with a deferred object to be sure they will load. This is using the jquery deferred method, but you could use any deferred library. I coded this from memory, so there may be some errors.
<img data-bind="attr: {src: $root.imagePath()}, style: { 'background-image': 'url('imagePath')' }" class="img-responsive">
var myController = function()
{
var self = this;
this.imagePath = ko.observable('myPath.png'); // Make the image url an observable
var getImagePath = function(path)
{
var imagePath = this.imagePath();
isLoaded(imagePath).done(function(result)
{
// The image will load fine, do nothing.
}).fail(function(e)
{
self.imagePath('defaultImageOnFail.png'); // replace the image if it fails to load
});
};
getImagePath();
};
var isLoaded = function(img)
{
var deferred = new $.Deferred();
var imgObj = $("<img src='"+img+"'/>");
if(imgObj.height > 0 || imgObj.width > 0)
{
deferred.resolve(true);
}
else
{
imgObj.on("load", function(e)
{
deferred.resolve(true);
});
imgObj.on("error", function(e)
{
console.info("VoteScreenController.isLoaded URL error");
deferred.reject();
});
}
return deferred.promise();
};