Javascript replace SEVERAL broken images until you find a good one [duplicate] - javascript

I have a web page that includes a bunch of images. Sometimes the image isn't available, so a broken image is displayed in the client's browser.
How do I use jQuery to get the set of images, filter it to broken images then replace the src?
--I thought it would be easier to do this with jQuery, but it turned out much easier to just use a pure JavaScript solution, that is, the one provided by Prestaul.

Handle the onError event for the image to reassign its source using JavaScript:
function imgError(image) {
image.onerror = "";
image.src = "/images/noimage.gif";
return true;
}
<img src="image.png" onerror="imgError(this);"/>
Or without a JavaScript function:
<img src="image.png" onError="this.onerror=null;this.src='/images/noimage.gif';" />
The following compatibility table lists the browsers that support the error facility:
http://www.quirksmode.org/dom/events/error.html

I use the built in error handler:
$("img").error(function () {
$(this).unbind("error").attr("src", "broken.gif");
});
Edit: The error() method is deprecated in jquery 1.8 and higher. Instead, you should use .on("error") instead:
$("img").on("error", function () {
$(this).attr("src", "broken.gif");
});

In case someone like me, tries to attach the error event to a dynamic HTML img tag, I'd like to point out that, there is a catch:
Apparently img error events don't bubble in most browsers, contrary to what the standard says.
So, something like the following will not work:
$(document).on('error', 'img', function () { ... })
Hope this will be helpful to someone else. I wish I had seen this here in this thread. But, I didn't. So, I am adding it

Here is a standalone solution:
$(window).load(function() {
$('img').each(function() {
if ( !this.complete
|| typeof this.naturalWidth == "undefined"
|| this.naturalWidth == 0 ) {
// image was broken, replace with your new image
this.src = 'http://www.tranism.com/weblog/images/broken_ipod.gif';
}
});
});

I believe this is what you're after: jQuery.Preload
Here's the example code from the demo, you specify the loading and not found images and you're all set:
jQuery('#images img').preload({
placeholder:'placeholder.jpg',
notFound:'notfound.jpg'
});

$(window).bind('load', function() {
$('img').each(function() {
if( (typeof this.naturalWidth != "undefined" && this.naturalWidth == 0)
|| this.readyState == 'uninitialized' ) {
$(this).attr('src', 'missing.jpg');
}
});
});
Source: http://www.developria.com/2009/03/jquery-quickie---broken-images.html

While the OP was looking to replace the SRC, I'm sure many people hitting this question may only wish to hide the broken image, in which case this simple solution worked great for me.
Using Inline JavaScript:
<img src="img.jpg" onerror="this.style.display='none';" />
Using External JavaScript:
var images = document.querySelectorAll('img');
for (var i = 0; i < images.length; i++) {
images[i].onerror = function() {
this.style.display='none';
}
}
<img src='img.jpg' />
Using Modern External JavaScript:
document.querySelectorAll('img').forEach((img) => {
img.onerror = function() {
this.style.display = 'none';
}
});
<img src='img.jpg' />
See browser support for NodeList.forEach and arrow functions.

Here is a quick-and-dirty way to replace all the broken images, and there is no need to change the HTML code ;)
codepen example
$("img").each(function(){
var img = $(this);
var image = new Image();
image.src = $(img).attr("src");
var no_image = "https://dummyimage.com/100x100/7080b5/000000&text=No+image";
if (image.naturalWidth == 0 || image.readyState == 'uninitialized'){
$(img).unbind("error").attr("src", no_image).css({
height: $(img).css("height"),
width: $(img).css("width"),
});
}
});

This is a crappy technique, but it's pretty much guaranteed:
<img onerror="this.parentNode.removeChild(this);">

I couldn't find a script to suit my needs, so I made a recursive function to check for broken images and attempt to reload them every four seconds until they are fixed.
I limited it to 10 attempts as if it's not loaded by then the image might not be present on server and the function would enter an infinite loop. I am still testing though. Feel free to tweak it :)
var retries = 0;
$.imgReload = function() {
var loaded = 1;
$("img").each(function() {
if (!this.complete || typeof this.naturalWidth == "undefined" || this.naturalWidth == 0) {
var src = $(this).attr("src");
var date = new Date();
$(this).attr("src", src + "?v=" + date.getTime()); //slightly change url to prevent loading from cache
loaded =0;
}
});
retries +=1;
if (retries < 10) { // If after 10 retries error images are not fixed maybe because they
// are not present on server, the recursion will break the loop
if (loaded == 0) {
setTimeout('$.imgReload()',4000); // I think 4 seconds is enough to load a small image (<50k) from a slow server
}
// All images have been loaded
else {
// alert("images loaded");
}
}
// If error images cannot be loaded after 10 retries
else {
// alert("recursion exceeded");
}
}
jQuery(document).ready(function() {
setTimeout('$.imgReload()',5000);
});

You can use GitHub's own fetch for this:
Frontend: https://github.com/github/fetch
or for Backend, a Node.js version: https://github.com/bitinn/node-fetch
fetch(url)
.then(function(res) {
if (res.status == '200') {
return image;
} else {
return placeholder;
}
}
Edit: This method is going to replace XHR and supposedly already has been in Chrome. To anyone reading this in the future, you may not need the aforementioned library included.

This is JavaScript, should be cross browser compatible, and delivers without the ugly markup onerror="":
var sPathToDefaultImg = 'http://cdn.sstatic.net/stackexchange/img/logos/so/so-icon.png',
validateImage = function( domImg ) {
oImg = new Image();
oImg.onerror = function() {
domImg.src = sPathToDefaultImg;
};
oImg.src = domImg.src;
},
aImg = document.getElementsByTagName( 'IMG' ),
i = aImg.length;
while ( i-- ) {
validateImage( aImg[i] );
}
CODEPEN:

This has been frustrating me for years. My CSS fix sets a background image on the img. When a dynamic image src doesn't load to the foreground, a placeholder is visible on the img's bg. This works if your images have a default size (e.g. height, min-height, width and/or min-width).
You'll see the broken image icon but it's an improvement. Tested down to IE9 successfully. iOS Safari and Chrome don't even show a broken icon.
.dynamicContainer img {
background: url('/images/placeholder.png');
background-size: contain;
}
Add a little animation to give src time to load without a background flicker. Chrome fades in the background smoothly but desktop Safari doesn't.
.dynamicContainer img {
background: url('/images/placeholder.png');
background-size: contain;
animation: fadein 1s;
}
#keyframes fadein {
0% { opacity: 0.0; }
50% { opacity: 0.5; }
100% { opacity: 1.0; }
}
.dynamicContainer img {
background: url('https://picsum.photos/id/237/200');
background-size: contain;
animation: fadein 1s;
}
#keyframes fadein {
0% {
opacity: 0.0;
}
50% {
opacity: 0.5;
}
100% {
opacity: 1.0;
}
}
img {
/* must define dimensions */
width: 200px;
height: 200px;
min-width: 200px;
min-height: 200px;
/* hides broken text */
color: transparent;
/* optional css below here */
display: block;
border: .2em solid black;
border-radius: 1em;
margin: 1em;
}
<div class="dynamicContainer">
<img src="https://picsum.photos/200" alt="Found image" />
<img src="https://picsumx.photos/200" alt="Not found image" />
</div>

If you have inserted your img with innerHTML, like: $("div").innerHTML = <img src="wrong-uri">, you can load another image if it fails doing, e.g, this:
<script>
function imgError(img) {
img.error="";
img.src="valid-uri";
}
</script>
<img src="wrong-uri" onerror="javascript:imgError(this)">
Why is javascript: _needed? Because scripts injected into the DOM via script tags in innerHTML are not run at the time they are injected, so you have to be explicit.

Better call using
jQuery(window).load(function(){
$.imgReload();
});
Because using document.ready doesn't necessary imply that images are loaded, only the HTML. Thus, there is no need for a delayed call.

CoffeeScript variant:
I made it to fix an issue with Turbolinks that causes the .error() method to get raised in Firefox sometimes even though the image is really there.
$("img").error ->
e = $(#).get 0
$(#).hide() if !$.browser.msie && (typeof this.naturalWidth == "undefined" || this.naturalWidth == 0)

By using Prestaul's answer, I added some checks and I prefer to use the jQuery way.
<img src="image1.png" onerror="imgError(this,1);"/>
<img src="image2.png" onerror="imgError(this,2);"/>
function imgError(image, type) {
if (typeof jQuery !== 'undefined') {
var imgWidth=$(image).attr("width");
var imgHeight=$(image).attr("height");
// Type 1 puts a placeholder image
// Type 2 hides img tag
if (type == 1) {
if (typeof imgWidth !== 'undefined' && typeof imgHeight !== 'undefined') {
$(image).attr("src", "http://lorempixel.com/" + imgWidth + "/" + imgHeight + "/");
} else {
$(image).attr("src", "http://lorempixel.com/200/200/");
}
} else if (type == 2) {
$(image).hide();
}
}
return true;
}

I found this post while looking at this other SO post. Below is a copy of the answer I gave there.
I know this is an old thread, but React has become popular and, perhaps, someone using React will come here looking for an answer to the same problem.
So, if you are using React, you can do something like the below, which was an answer original provided by Ben Alpert of the React team here
getInitialState: function(event) {
return {image: "http://example.com/primary_image.jpg"};
},
handleError: function(event) {
this.setState({image: "http://example.com/failover_image.jpg"});
},
render: function() {
return (
<img onError={this.handleError} src={src} />;
);
}

I created a fiddle to replace the broken image using "onerror" event.
This may help you.
//the placeholder image url
var defaultUrl = "url('https://sadasd/image02.png')";
$('div').each(function(index, item) {
var currentUrl = $(item).css("background-image").replace(/^url\(['"](.+)['"]\)/, '$1');
$('<img>', {
src: currentUrl
}).on("error", function(e) {
$this = $(this);
$this.css({
"background-image": defaultUrl
})
e.target.remove()
}.bind(this))
})

Here is an example using the HTML5 Image object wrapped by JQuery. Call the load function for the primary image URL and if that load causes an error, replace the src attribute of the image with a backup URL.
function loadImageUseBackupUrlOnError(imgId, primaryUrl, backupUrl) {
var $img = $('#' + imgId);
$(new Image()).load().error(function() {
$img.attr('src', backupUrl);
}).attr('src', primaryUrl)
}
<img id="myImage" src="primary-image-url"/>
<script>
loadImageUseBackupUrlOnError('myImage','primary-image-url','backup-image-url');
</script>

Pure JS.
My task was: if image 'bl-once.png' is empty -> insert the first one (that hasn't 404 status) image from array list (in current dir):
<img src="http://localhost:63342/GetImage/bl-once.png" width="200" onerror="replaceEmptyImage.insertImg(this)">
Maybe it needs to be improved, but:
var srcToInsertArr = ['empty1.png', 'empty2.png', 'needed.png', 'notActual.png']; // try to insert one by one img from this array
var path;
var imgNotFounded = true; // to mark when success
var replaceEmptyImage = {
insertImg: function (elem) {
if (srcToInsertArr.length == 0) { // if there are no more src to try return
return "no-image.png";
}
if(!/undefined/.test(elem.src)) { // remember path
path = elem.src.split("/").slice(0, -1).join("/"); // "http://localhost:63342/GetImage"
}
var url = path + "/" + srcToInsertArr[0];
srcToInsertArr.splice(0, 1); // tried 1 src
if(imgNotFounded){ // while not success
replaceEmptyImage.getImg(url, path, elem); // CALL GET IMAGE
}
},
getImg: function (src, path, elem) { // GET IMAGE
if (src && path && elem) { // src = "http://localhost:63342/GetImage/needed.png"
var pathArr = src.split("/"); // ["http:", "", "localhost:63342", "GetImage", "needed.png"]
var name = pathArr[pathArr.length - 1]; // "needed.png"
xhr = new XMLHttpRequest();
xhr.open('GET', src, true);
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.status == 200) {
elem.src = src; // insert correct src
imgNotFounded = false; // mark success
}
else {
console.log(name + " doesn't exist!");
elem.onerror();
}
}
}
}
};
So, it will insert correct 'needed.png' to my src or 'no-image.png' from current dir.

(window.jQuery || window.Zepto).fn.fallback = function (fallback) {
return this.one('error', function () {
var self = this;
this.src = (fallback || 'http://lorempixel.com/$width/$height').replace(
/\$(\w+)/g, function (m, t) { return self[t] || ''; }
);
});
};
You can pass a placeholder path and acces in it all properties from the failed image object via $*:
$('img').fallback('http://dummyimage.com/$widthx$height&text=$src');
http://jsfiddle.net/ARTsinn/Cu4Zn/

For React Developers:
<img
src={"https://urlto/yourimage.png"} // <--- If this image src fail to load, onError function will be called, where you can add placeholder image or any image you want to load
width={200}
alt={"Image"}
onError={(event) => {
event.target.onerror = "";
event.target.src = "anyplaceholderimageUrlorPath"
return true;
}}
/>

I am not sure if there is a better way, but I can think of a hack to get it - you could Ajax post to the img URL, and parse the response to see if the image actually came back. If it came back as a 404 or something, then swap out the img. Though I expect this to be quite slow.

I solved my problem with these two simple functions:
function imgExists(imgPath) {
var http = jQuery.ajax({
type:"HEAD",
url: imgPath,
async: false
});
return http.status != 404;
}
function handleImageError() {
var imgPath;
$('img').each(function() {
imgPath = $(this).attr('src');
if (!imgExists(imgPath)) {
$(this).attr('src', 'images/noimage.jpg');
}
});
}

jQuery 1.8
// If missing.png is missing, it is replaced by replacement.png
$( "img" )
.error(function() {
$( this ).attr( "src", "replacement.png" );
})
.attr( "src", "missing.png" );
jQuery 3
// If missing.png is missing, it is replaced by replacement.png
$( "img" )
.on("error", function() {
$( this ).attr( "src", "replacement.png" );
})
.attr( "src", "missing.png" );
reference

Sometimes using the error event is not feasible, e.g. because you're trying to do something on a page that’s already loaded, such as when you’re running code via the console, a bookmarklet, or a script loaded asynchronously. In that case, checking that img.naturalWidth and img.naturalHeight are 0 seems to do the trick.
For example, here's a snippet to reload all broken images from the console:
$$("img").forEach(img => {
if (!img.naturalWidth && !img.naturalHeight) {
img.src = img.src;
}
}

I think I have a more elegant way with event delegation and event capturing on window's error even when the backup image fail to load.
img {
width: 100px;
height: 100px;
}
<script>
window.addEventListener('error', windowErrorCb, {
capture: true
}, true)
function windowErrorCb(event) {
let target = event.target
let isImg = target.tagName.toLowerCase() === 'img'
if (isImg) {
imgErrorCb()
return
}
function imgErrorCb() {
let isImgErrorHandled = target.hasAttribute('data-src-error')
if (!isImgErrorHandled) {
target.setAttribute('data-src-error', 'handled')
target.src = 'backup.png'
} else {
//anything you want to do
console.log(target.alt, 'both origin and backup image fail to load!');
}
}
}
</script>
<img id="img" src="error1.png" alt="error1">
<img id="img" src="error2.png" alt="error2">
<img id="img" src="https://i.stack.imgur.com/ZXCE2.jpg" alt="avatar">
The point is :
Put the code in the head and executed as the first inline script. So, it will listen the errors happen after the script.
Use event capturing to catch the errors, especially for those events which don't bubble.
Use event delegation which avoids binding events on each image.
Give the error img element an attribute after giving them a backup.png to avoid disappearance of the backup.png and subsequent infinite loop like below:
img error->backup.png->error->backup.png->error->,,,,,

If the image cannot be loaded (for example, because it is not present at the supplied URL), image URL will be changed into default,
For more about .error()
$('img').on('error', function (e) {
$(this).attr('src', 'broken.png');
});

I got the same problem. This code works well on my case.
// Replace broken images by a default img
$('img').each(function(){
if($(this).attr('src') === ''){
this.src = '/default_feature_image.png';
}
});

Related

How to put a default image if img tag is empty using jquery [duplicate]

I have a web page that includes a bunch of images. Sometimes the image isn't available, so a broken image is displayed in the client's browser.
How do I use jQuery to get the set of images, filter it to broken images then replace the src?
--I thought it would be easier to do this with jQuery, but it turned out much easier to just use a pure JavaScript solution, that is, the one provided by Prestaul.
Handle the onError event for the image to reassign its source using JavaScript:
function imgError(image) {
image.onerror = "";
image.src = "/images/noimage.gif";
return true;
}
<img src="image.png" onerror="imgError(this);"/>
Or without a JavaScript function:
<img src="image.png" onError="this.onerror=null;this.src='/images/noimage.gif';" />
The following compatibility table lists the browsers that support the error facility:
http://www.quirksmode.org/dom/events/error.html
I use the built in error handler:
$("img").error(function () {
$(this).unbind("error").attr("src", "broken.gif");
});
Edit: The error() method is deprecated in jquery 1.8 and higher. Instead, you should use .on("error") instead:
$("img").on("error", function () {
$(this).attr("src", "broken.gif");
});
In case someone like me, tries to attach the error event to a dynamic HTML img tag, I'd like to point out that, there is a catch:
Apparently img error events don't bubble in most browsers, contrary to what the standard says.
So, something like the following will not work:
$(document).on('error', 'img', function () { ... })
Hope this will be helpful to someone else. I wish I had seen this here in this thread. But, I didn't. So, I am adding it
Here is a standalone solution:
$(window).load(function() {
$('img').each(function() {
if ( !this.complete
|| typeof this.naturalWidth == "undefined"
|| this.naturalWidth == 0 ) {
// image was broken, replace with your new image
this.src = 'http://www.tranism.com/weblog/images/broken_ipod.gif';
}
});
});
I believe this is what you're after: jQuery.Preload
Here's the example code from the demo, you specify the loading and not found images and you're all set:
jQuery('#images img').preload({
placeholder:'placeholder.jpg',
notFound:'notfound.jpg'
});
$(window).bind('load', function() {
$('img').each(function() {
if( (typeof this.naturalWidth != "undefined" && this.naturalWidth == 0)
|| this.readyState == 'uninitialized' ) {
$(this).attr('src', 'missing.jpg');
}
});
});
Source: http://www.developria.com/2009/03/jquery-quickie---broken-images.html
While the OP was looking to replace the SRC, I'm sure many people hitting this question may only wish to hide the broken image, in which case this simple solution worked great for me.
Using Inline JavaScript:
<img src="img.jpg" onerror="this.style.display='none';" />
Using External JavaScript:
var images = document.querySelectorAll('img');
for (var i = 0; i < images.length; i++) {
images[i].onerror = function() {
this.style.display='none';
}
}
<img src='img.jpg' />
Using Modern External JavaScript:
document.querySelectorAll('img').forEach((img) => {
img.onerror = function() {
this.style.display = 'none';
}
});
<img src='img.jpg' />
See browser support for NodeList.forEach and arrow functions.
Here is a quick-and-dirty way to replace all the broken images, and there is no need to change the HTML code ;)
codepen example
$("img").each(function(){
var img = $(this);
var image = new Image();
image.src = $(img).attr("src");
var no_image = "https://dummyimage.com/100x100/7080b5/000000&text=No+image";
if (image.naturalWidth == 0 || image.readyState == 'uninitialized'){
$(img).unbind("error").attr("src", no_image).css({
height: $(img).css("height"),
width: $(img).css("width"),
});
}
});
This is a crappy technique, but it's pretty much guaranteed:
<img onerror="this.parentNode.removeChild(this);">
I couldn't find a script to suit my needs, so I made a recursive function to check for broken images and attempt to reload them every four seconds until they are fixed.
I limited it to 10 attempts as if it's not loaded by then the image might not be present on server and the function would enter an infinite loop. I am still testing though. Feel free to tweak it :)
var retries = 0;
$.imgReload = function() {
var loaded = 1;
$("img").each(function() {
if (!this.complete || typeof this.naturalWidth == "undefined" || this.naturalWidth == 0) {
var src = $(this).attr("src");
var date = new Date();
$(this).attr("src", src + "?v=" + date.getTime()); //slightly change url to prevent loading from cache
loaded =0;
}
});
retries +=1;
if (retries < 10) { // If after 10 retries error images are not fixed maybe because they
// are not present on server, the recursion will break the loop
if (loaded == 0) {
setTimeout('$.imgReload()',4000); // I think 4 seconds is enough to load a small image (<50k) from a slow server
}
// All images have been loaded
else {
// alert("images loaded");
}
}
// If error images cannot be loaded after 10 retries
else {
// alert("recursion exceeded");
}
}
jQuery(document).ready(function() {
setTimeout('$.imgReload()',5000);
});
You can use GitHub's own fetch for this:
Frontend: https://github.com/github/fetch
or for Backend, a Node.js version: https://github.com/bitinn/node-fetch
fetch(url)
.then(function(res) {
if (res.status == '200') {
return image;
} else {
return placeholder;
}
}
Edit: This method is going to replace XHR and supposedly already has been in Chrome. To anyone reading this in the future, you may not need the aforementioned library included.
This is JavaScript, should be cross browser compatible, and delivers without the ugly markup onerror="":
var sPathToDefaultImg = 'http://cdn.sstatic.net/stackexchange/img/logos/so/so-icon.png',
validateImage = function( domImg ) {
oImg = new Image();
oImg.onerror = function() {
domImg.src = sPathToDefaultImg;
};
oImg.src = domImg.src;
},
aImg = document.getElementsByTagName( 'IMG' ),
i = aImg.length;
while ( i-- ) {
validateImage( aImg[i] );
}
CODEPEN:
This has been frustrating me for years. My CSS fix sets a background image on the img. When a dynamic image src doesn't load to the foreground, a placeholder is visible on the img's bg. This works if your images have a default size (e.g. height, min-height, width and/or min-width).
You'll see the broken image icon but it's an improvement. Tested down to IE9 successfully. iOS Safari and Chrome don't even show a broken icon.
.dynamicContainer img {
background: url('/images/placeholder.png');
background-size: contain;
}
Add a little animation to give src time to load without a background flicker. Chrome fades in the background smoothly but desktop Safari doesn't.
.dynamicContainer img {
background: url('/images/placeholder.png');
background-size: contain;
animation: fadein 1s;
}
#keyframes fadein {
0% { opacity: 0.0; }
50% { opacity: 0.5; }
100% { opacity: 1.0; }
}
.dynamicContainer img {
background: url('https://picsum.photos/id/237/200');
background-size: contain;
animation: fadein 1s;
}
#keyframes fadein {
0% {
opacity: 0.0;
}
50% {
opacity: 0.5;
}
100% {
opacity: 1.0;
}
}
img {
/* must define dimensions */
width: 200px;
height: 200px;
min-width: 200px;
min-height: 200px;
/* hides broken text */
color: transparent;
/* optional css below here */
display: block;
border: .2em solid black;
border-radius: 1em;
margin: 1em;
}
<div class="dynamicContainer">
<img src="https://picsum.photos/200" alt="Found image" />
<img src="https://picsumx.photos/200" alt="Not found image" />
</div>
If you have inserted your img with innerHTML, like: $("div").innerHTML = <img src="wrong-uri">, you can load another image if it fails doing, e.g, this:
<script>
function imgError(img) {
img.error="";
img.src="valid-uri";
}
</script>
<img src="wrong-uri" onerror="javascript:imgError(this)">
Why is javascript: _needed? Because scripts injected into the DOM via script tags in innerHTML are not run at the time they are injected, so you have to be explicit.
Better call using
jQuery(window).load(function(){
$.imgReload();
});
Because using document.ready doesn't necessary imply that images are loaded, only the HTML. Thus, there is no need for a delayed call.
CoffeeScript variant:
I made it to fix an issue with Turbolinks that causes the .error() method to get raised in Firefox sometimes even though the image is really there.
$("img").error ->
e = $(#).get 0
$(#).hide() if !$.browser.msie && (typeof this.naturalWidth == "undefined" || this.naturalWidth == 0)
By using Prestaul's answer, I added some checks and I prefer to use the jQuery way.
<img src="image1.png" onerror="imgError(this,1);"/>
<img src="image2.png" onerror="imgError(this,2);"/>
function imgError(image, type) {
if (typeof jQuery !== 'undefined') {
var imgWidth=$(image).attr("width");
var imgHeight=$(image).attr("height");
// Type 1 puts a placeholder image
// Type 2 hides img tag
if (type == 1) {
if (typeof imgWidth !== 'undefined' && typeof imgHeight !== 'undefined') {
$(image).attr("src", "http://lorempixel.com/" + imgWidth + "/" + imgHeight + "/");
} else {
$(image).attr("src", "http://lorempixel.com/200/200/");
}
} else if (type == 2) {
$(image).hide();
}
}
return true;
}
I found this post while looking at this other SO post. Below is a copy of the answer I gave there.
I know this is an old thread, but React has become popular and, perhaps, someone using React will come here looking for an answer to the same problem.
So, if you are using React, you can do something like the below, which was an answer original provided by Ben Alpert of the React team here
getInitialState: function(event) {
return {image: "http://example.com/primary_image.jpg"};
},
handleError: function(event) {
this.setState({image: "http://example.com/failover_image.jpg"});
},
render: function() {
return (
<img onError={this.handleError} src={src} />;
);
}
I created a fiddle to replace the broken image using "onerror" event.
This may help you.
//the placeholder image url
var defaultUrl = "url('https://sadasd/image02.png')";
$('div').each(function(index, item) {
var currentUrl = $(item).css("background-image").replace(/^url\(['"](.+)['"]\)/, '$1');
$('<img>', {
src: currentUrl
}).on("error", function(e) {
$this = $(this);
$this.css({
"background-image": defaultUrl
})
e.target.remove()
}.bind(this))
})
Here is an example using the HTML5 Image object wrapped by JQuery. Call the load function for the primary image URL and if that load causes an error, replace the src attribute of the image with a backup URL.
function loadImageUseBackupUrlOnError(imgId, primaryUrl, backupUrl) {
var $img = $('#' + imgId);
$(new Image()).load().error(function() {
$img.attr('src', backupUrl);
}).attr('src', primaryUrl)
}
<img id="myImage" src="primary-image-url"/>
<script>
loadImageUseBackupUrlOnError('myImage','primary-image-url','backup-image-url');
</script>
Pure JS.
My task was: if image 'bl-once.png' is empty -> insert the first one (that hasn't 404 status) image from array list (in current dir):
<img src="http://localhost:63342/GetImage/bl-once.png" width="200" onerror="replaceEmptyImage.insertImg(this)">
Maybe it needs to be improved, but:
var srcToInsertArr = ['empty1.png', 'empty2.png', 'needed.png', 'notActual.png']; // try to insert one by one img from this array
var path;
var imgNotFounded = true; // to mark when success
var replaceEmptyImage = {
insertImg: function (elem) {
if (srcToInsertArr.length == 0) { // if there are no more src to try return
return "no-image.png";
}
if(!/undefined/.test(elem.src)) { // remember path
path = elem.src.split("/").slice(0, -1).join("/"); // "http://localhost:63342/GetImage"
}
var url = path + "/" + srcToInsertArr[0];
srcToInsertArr.splice(0, 1); // tried 1 src
if(imgNotFounded){ // while not success
replaceEmptyImage.getImg(url, path, elem); // CALL GET IMAGE
}
},
getImg: function (src, path, elem) { // GET IMAGE
if (src && path && elem) { // src = "http://localhost:63342/GetImage/needed.png"
var pathArr = src.split("/"); // ["http:", "", "localhost:63342", "GetImage", "needed.png"]
var name = pathArr[pathArr.length - 1]; // "needed.png"
xhr = new XMLHttpRequest();
xhr.open('GET', src, true);
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.status == 200) {
elem.src = src; // insert correct src
imgNotFounded = false; // mark success
}
else {
console.log(name + " doesn't exist!");
elem.onerror();
}
}
}
}
};
So, it will insert correct 'needed.png' to my src or 'no-image.png' from current dir.
(window.jQuery || window.Zepto).fn.fallback = function (fallback) {
return this.one('error', function () {
var self = this;
this.src = (fallback || 'http://lorempixel.com/$width/$height').replace(
/\$(\w+)/g, function (m, t) { return self[t] || ''; }
);
});
};
You can pass a placeholder path and acces in it all properties from the failed image object via $*:
$('img').fallback('http://dummyimage.com/$widthx$height&text=$src');
http://jsfiddle.net/ARTsinn/Cu4Zn/
For React Developers:
<img
src={"https://urlto/yourimage.png"} // <--- If this image src fail to load, onError function will be called, where you can add placeholder image or any image you want to load
width={200}
alt={"Image"}
onError={(event) => {
event.target.onerror = "";
event.target.src = "anyplaceholderimageUrlorPath"
return true;
}}
/>
I am not sure if there is a better way, but I can think of a hack to get it - you could Ajax post to the img URL, and parse the response to see if the image actually came back. If it came back as a 404 or something, then swap out the img. Though I expect this to be quite slow.
I solved my problem with these two simple functions:
function imgExists(imgPath) {
var http = jQuery.ajax({
type:"HEAD",
url: imgPath,
async: false
});
return http.status != 404;
}
function handleImageError() {
var imgPath;
$('img').each(function() {
imgPath = $(this).attr('src');
if (!imgExists(imgPath)) {
$(this).attr('src', 'images/noimage.jpg');
}
});
}
jQuery 1.8
// If missing.png is missing, it is replaced by replacement.png
$( "img" )
.error(function() {
$( this ).attr( "src", "replacement.png" );
})
.attr( "src", "missing.png" );
jQuery 3
// If missing.png is missing, it is replaced by replacement.png
$( "img" )
.on("error", function() {
$( this ).attr( "src", "replacement.png" );
})
.attr( "src", "missing.png" );
reference
Sometimes using the error event is not feasible, e.g. because you're trying to do something on a page that’s already loaded, such as when you’re running code via the console, a bookmarklet, or a script loaded asynchronously. In that case, checking that img.naturalWidth and img.naturalHeight are 0 seems to do the trick.
For example, here's a snippet to reload all broken images from the console:
$$("img").forEach(img => {
if (!img.naturalWidth && !img.naturalHeight) {
img.src = img.src;
}
}
I think I have a more elegant way with event delegation and event capturing on window's error even when the backup image fail to load.
img {
width: 100px;
height: 100px;
}
<script>
window.addEventListener('error', windowErrorCb, {
capture: true
}, true)
function windowErrorCb(event) {
let target = event.target
let isImg = target.tagName.toLowerCase() === 'img'
if (isImg) {
imgErrorCb()
return
}
function imgErrorCb() {
let isImgErrorHandled = target.hasAttribute('data-src-error')
if (!isImgErrorHandled) {
target.setAttribute('data-src-error', 'handled')
target.src = 'backup.png'
} else {
//anything you want to do
console.log(target.alt, 'both origin and backup image fail to load!');
}
}
}
</script>
<img id="img" src="error1.png" alt="error1">
<img id="img" src="error2.png" alt="error2">
<img id="img" src="https://i.stack.imgur.com/ZXCE2.jpg" alt="avatar">
The point is :
Put the code in the head and executed as the first inline script. So, it will listen the errors happen after the script.
Use event capturing to catch the errors, especially for those events which don't bubble.
Use event delegation which avoids binding events on each image.
Give the error img element an attribute after giving them a backup.png to avoid disappearance of the backup.png and subsequent infinite loop like below:
img error->backup.png->error->backup.png->error->,,,,,
If the image cannot be loaded (for example, because it is not present at the supplied URL), image URL will be changed into default,
For more about .error()
$('img').on('error', function (e) {
$(this).attr('src', 'broken.png');
});
I got the same problem. This code works well on my case.
// Replace broken images by a default img
$('img').each(function(){
if($(this).attr('src') === ''){
this.src = '/default_feature_image.png';
}
});

Image Preloader Getting Stuck in IE8

I've searched high and low for a solution to this problem, extremely grateful to anyone who can shed some light.
Below is a simplified version of a function used to preload 25 images. It works fantastically well in IE9-10 and in Chrome.
In IE8 it gets partway through loading the images (e.g. 10/25) then just stops. If I then refresh the browser it will continue to load images, taking ~3 refreshes to complete. This should not be necessary!
On the odd occasion it does succeed in loading all images in one go. I tried to avoid using .load() as I've read it can be problematic in IE yet here I am just the same! I also tried using different images to rule out the image format as the cause.
I'm looking for either a fix to the below code or a better solution if someone has one.
Many thanks.
function loadAllImages() {
var imgArray = [ //image SRCs stored in array
'imgsrc01.jpg',
'imgsrc02.jpg',
'imgsrc03.jpg',
...
'imgsrc25.jpg'];
var noFinalImg = imgArray.length - 1;
var i = -1;
var waitTime = 10000;
if (imgArray.length === 0) {
return;
} else {
loadImage();
};
function loadImage() {
i++;
var url = imgArray[i];
var img = new Image();
var timer;
img.src = url;
img.id = "thumb" + i;
// image loaded
if (img.complete || img.readyState === 4) {
//Call image loaded function
console.log(i + "/" + noFinalImg);
//equivalent to loop next
if (i !== noFinalImg) {
loadImage();
};
// image not loaded
} else {
// handle 404 using setTimeout set at waitTime
timer = setTimeout(function () {
if (i !== noFinalImg) {
loadImage();
};
$(img).unbind('error load onreadystate');
}, waitTime);
$(img).bind('error load onreadystatechange', function (e) {
clearTimeout(timer);
if (e.type !== 'error') {
//Call image loaded function
console.log(i + "/" + noFinalImg);
};
if (i !== noFinalImg) {
loadImage();
};
});
};
};
};

Making a button switch out an image on click

There may be an easier way to do this, like with CSS, I think, pseudo-classes or whatever
but anyway for some reason I already wrote this function.
On mouseup it should switch the image back to normal. (Note that the images have the same names except for mouse down the .png is switched with ed.png and the opposite is done for document.mouseup. But document.mouseup isn't even working).
$.fn.buttonify = function () {
console.log("buttonification successful");
var that;
var source;
this.mousedown(function (event) {
var top_value;
var left_value;
that = this;
if (event.which == 1) { //if it is a left click
source = this.src.substring(0, this.src.length - 4);
source += "ed.png";
this.src = source;
console.log(this.src);
}
});
$(document).mouseup(function () {
if (that == null) {
console.log("returninurnging");
return;
}
console.log(source);
source = source.substring(0, source.length - 6); //very questionable code
source += ".png";
that.src = source;
console.log(that.src);
that = null;
});
}
When the mouse is lifted outside the click area, it does not change the image.
Otherwise it works. :(
What do U really want?
<img src="image1.jpg" id="image">
<input type="button" value="whatever" onMouseDown="change()" onMouseUp="undochange()">
JAVASCRIPT
function change()
{
image = document.getElementById ("image");
image.src="image2.jpg";
}
function undochange()
{
image = document.getElementById ("image");
image.src="image1.jpg";
}

jQuery, how to preload images and be notified when images are loaded?

i am still new to all the Javascript stuffs and i have to be honest that i have not yet experimented anything concerning my question.
I would like to know if there is a way or a plugin with jQuery to preloaded multiples images and call a function when the images are loaded?
You can use the load event for images, but they have a number of cross browser issues (it depends on what platforms you're targeting).
var allImgs = $("#container img").filter(function() { return ! this.complete; });
var allImgsLength = allImgs.length;
allImgs.on("load", function() {
if (--allImgsLength) {
// Images have loaded.
}
});
If you don't mind including a plugin, there is waitForImages, (disclaimer: written by me) which handles this relatively nicely. :)
You'd use it like...
$("#container").waitForImages(function() {
// Images have loaded.
});
If you don't want to use jQuery at all, you can adapt the first example. If you're only targeting modern browsers...
var allImgs = [].filter.call(document.querySelectorAll("#container img"),
function(img) {
return ! img.complete;
});
var allImgsLength = allImgs.length;
[].forEach.call(allImgs, function(img) {
img.addEventListener(function() {
if (--allImgsLength) {
// Images have loaded.
}
});
});
If you had to support old browsers...
var allImgs = document.getElementById("container").getElementsByTagName("img");
var allImgsLength = allImgs.length;
var i;
var eventCallback = function() {
if (--allImgsLength) {
// Images have loaded.
}
};
for (i = 0; i < allImgsLength; i++) {
if (allImgs[i].complete) {
allImgsLength--;
}
if (allImgs[i].addEventListener) {
allImgs[i].addEventListener("load", eventCallback);
} elseif (allImgs[i].attachEvent) {
allImgs[i].attachEvent("onload", eventCallback);
} else {
allImgs[i].onload = eventCallback;
}
}

Detecting an image 404 in Javascript

After a user uploads a file we have to do some additional processing with the images such as resizing and upload to S3. This can take up to 10 extra seconds. Obviously we do this in a background. However, we want to show the user the result page immediately and simply show spinners in place until the images arrive in their permanent home on s3.
I'm looking for a way to detect that a certain image failed to load correctly (404) in a cross browser way. If that happens, we want to use JS to show a spinner in it's place and reload the image every few seconds until it can be successfully loaded from s3.
Handle the <img> element's onerror event.
First option:
<img src="picture1.gif" onerror="this.onerror=null;this.src='missing.gif';"/>
Second option:
<html>
<head>
<script type="text/javascript">
function ImgError(source){
source.src = "/noimage.gif";
source.onerror = "";
return true;
}
</script>
</head>
<body>
<img src="image_example1.jpg" onerror="ImgError(this)" />
</body>
</html>
PS: it's pure javascript! you don't need any libraries. (Vanilla JS)
Example in Fidler
https://jsfiddle.net/dorathoto/8z4Ltzp8/71/
From: http://lucassmith.name/2008/11/is-my-image-loaded.html
// First a couple helper functions
function $(id) {
return !id || id.nodeType === 1 ? id : document.getElementById(id);
}
function isType(o,t) { return (typeof o).indexOf(t.charAt(0).toLowerCase()) === 0;}
// Here's the meat and potatoes
function image(src,cfg) { var img, prop, target;
cfg = cfg || (isType(src,'o') ? src : {});
img = $(src);
if (img) {
src = cfg.src || img.src;
} else {
img = document.createElement('img');
src = src || cfg.src;
}
if (!src) {
return null;
}
prop = isType(img.naturalWidth,'u') ? 'width' : 'naturalWidth';
img.alt = cfg.alt || img.alt;
// Add the image and insert if requested (must be on DOM to load or
// pull from cache)
img.src = src;
target = $(cfg.target);
if (target) {
target.insertBefore(img, $(cfg.insertBefore) || null);
}
// Loaded?
if (img.complete) {
if (img[prop]) {
if (isType(cfg.success,'f')) {
cfg.success.call(img);
}
} else {
if (isType(cfg.failure,'f')) {
cfg.failure.call(img);
}
}
} else {
if (isType(cfg.success,'f')) {
img.onload = cfg.success;
}
if (isType(cfg.failure,'f')) {
img.onerror = cfg.failure;
}
}
return img;
}
And here how to use it:
image('imgId',{
success : function () { alert(this.width); },
failure : function () { alert('Damn your eyes!'); },
});
image('http://somedomain.com/image/typooed_url.jpg', {
success : function () {...},
failure : function () {...},
target : 'myContainerId',
insertBefore : 'someChildOfmyContainerId'
});
just bind the attr trigger on the error event.
$(myimgvar).bind('error',function(ev){
//error has been thrown
$(this).attr('src','/path/to/no-artwork-available.jpg');
}).attr('src',urlvar);
This worked for me (mine is in coffeescript). You'll need to replace with a spinner instead, of course.
checkImages = ->
$("img").each ->
$(this).error ->
$(this).attr("src", "../default/image.jpg")
$(document).on('page:load', checkImages)
I'm guessing the javascript equivalent is something like
function checkImages() {
$("img").each(function() {
$(this).error(function() {
$(this).attr("src", "../default/image.jpg");
});
});
};
$(document).on("page:load", checkImages);
I just did
if ($('#img')[0].naturalWidth > 0) {
as i noticed there was no naturalWidth if the image 404'd.
However, i can understand wanting to use a method above.

Categories