Reputation: 6110
I have application where images should be set in some of the pages. These images are on the server and each image has unique ID appended. Here is example of the image names:
AA_image1.jpg
AB_image1.jpg
AC_image1.jpg
AD_image1.jpg
AE_image1.jpg
I seen what previous developers did and how they checked the image existance. They used JSON file that has id and image name. Here is example of that code:
var images = [{
"id": "AA",
"image": "AA_image1.jpg"
},
{
"id": "AB",
"image": "AB_image1.jpg"
},
{
"id": "AC",
"image": "AC_image1.jpg"
},
{
"id": "AD",
"image": "AD_image1.jpg"
},
{
"id": "AE",
"image": "AE_image1.jpg"
}
];
var imgID = "AC";
var imgPrimary = "AC_image1.jpg";
var found = false;
var imgDefault = "default.jpg";
for (i = 1; i < images.length; i++) {
if (images[i].id == imgID && (images[i].image).toLowerCase() == (imgID + '_image1.jpg').toLowerCase()) {
found = true;
break;
}
}
if (found === true) {
console.log(imgPrimary);
} else {
console.log(imgDefault);
}
The example above seems pretty simple but my concern is what if image get removed from the folder and JSON file is not updated? In that case we would load the image that do not exist instead of default image. I'm wondering if this approach would be better:
var imgID = "AC";
var imgNames = [imgID + '_image1','default'];
var imgResults = {};
for(var i = 0; i < imgNames.length; i++){
checkImage( imgNames[i] );
}
function checkImage( imgName, keyName ) {
$.ajax({
type: "GET",
async: true,
url: "images/"+imgName+".jpg",
}).done(function(message,text,jqXHR){
imgResults[imgName] = true;
}).fail(function(jqXHR, textStatus, errorThrown){
imgResults[imgName] = false;
});
}
Here is example of imgResults
after the process was completed:
console.log(imgResults);
Console result:
{
"default": true,
"AG_image1": false
}
The only problem I'm experiencing with the second example is that if I try to check the result based on the key I'm getting undefined. Here is example:
console.log(imgResults["AG_image1"]);
This is result in the console:
undefined
Between the two I'm not sure which one is better and more secure. If anyone have suggestions please let me know.
Upvotes: 1
Views: 1324
Reputation: 23416
This is probably the shortest possible code to handle lost images in JS. imagePad
is a reference to the DOM element in which you want to show the image.
var img = new Image();
img.addEventListener('error', function (e) {
// Image not found, show the default
this.src = iconBase + 'gen.png';
});
img.addEventListener('load', function () {
imagePad.appendChild(img);
}
img.src = 'the_given_src';
Upvotes: 2
Reputation:
The reason that you're unable to search the imgResults
object when you are trying is because AJAX requests are asynchronous, and the request has not completed when you're trying to access the results. You need to wait for the request to complete before continuing.
The reason that console.log
shows the results is because console.log
is lazy and doesn't evaluate the object until you expand it in dev tools, console.dir
will show you an empty object at that point.
Furthermore, as you have multiple requests that you want to wait for, you'll want to create an array of promises where each promise corresponds to the load/failure of each request, then use Promise.all
to wait for all the promises to complete before operating on the results.
To verify that a resource exists, you can use a HEAD
request instead of a GET
request, such as to prevent loading the entire image for no reason. There's also no need to depend on a massive library such as jQuery for AJAX requests as XMLHttpRequest is very well supported in this day and age.
class ResourceValidator extends EventTarget {
constructor(target) {
super()
this.target = target
this._ready = false
this._valid = false
this.validate()
}
validate() {
const request = new XMLHttpRequest()
request.addEventListener('load', event => {
this._ready = true
this._valid = true
this.dispatchEvent(new Event('ready'))
})
request.addEventListener('error', event => {
this._ready = true
this.dispatchEvent(new Event('ready'))
})
request.open('HEAD', this.target)
request.send()
}
get ready() {
return new Promise(resolve => {
if(this._ready === true) resolve(true)
else this.addEventListener('ready', _ => resolve(true))
})
}
get valid() {
return new Promise(resolve => {
if(this._ready === true) resolve(this._valid)
else this.addEventListener('ready', _ => resolve(this._valid))
})
}
}
async function validateImageSources(sources) {
const results = {}
const promises = []
for(let source of sources) {
const validator = new ResourceValidator(source)
const promise = validator.valid
promise.then(valid => results[source] = valid)
promises.push(promise)
}
await Promise.all(promises)
return results
}
validateImageSources([
'https://picsum.photos/200',
'https://nosuchaddress.io/image.png'
]).then(results => {
console.log(results)
console.log(results['https://picsum.photos/200'])
})
Upvotes: 1