-3

I was going off of this thread:

Check if image exists on server using JavaScript?

This is the code I tried, it always returns "okay" I'm thinking this is because of the asynchronous/synchronous. It was set to false, but I set it to true because of a warning I saw in the console.

How do I get it to not say okay? If I try anything above 3 in the array source it gives 404 error in the console but after seeing "okay"

<script>
  var photosArray = ["1.jpg", "2.jpg", "3.jpg", "4.jpg"];
  var src = photosArray[5];
  console.log('src: ' + src);
  function imageExists(src){
    var http = new XMLHttpRequest();
    http.open('HEAD', src, true);
    http.send();
    if (http.status !== 404) {
      console.log('okay');
      return "okay";
    }
    else {
      console.log('fail');
      return "fail";
    }
    // return http.status != 404; original code's return
  }
  imageExists(src);
</script>
Community
  • 1
  • 1
  • Why is the src pointing to 5th index of the array when there is only max 3 indexes – Rajshekar Reddy Oct 29 '16 at 03:10
  • 2
    Possible duplicate of [How do I return the response from an asynchronous call?](http://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – CBroe Oct 29 '16 at 03:14
  • @Reddy because I know it doesn't exist so I want it to fail on purpose – joehungjohn Oct 29 '16 at 03:40
  • @Reddy In retrospect... (cool word) I think I get what you mean. All four of those photo sources I defined in the array exist, they're in the same folder. So I should pull one of them out and still use a link and see what happens as opposed to no name/undefined value. – joehungjohn Oct 29 '16 at 03:53

2 Answers2

3

Among other things, you need an event handler to execute the test.

<script>
 var photosArray = ["1.jpg", "2.jpg", "3.jpg", "4.jpg"];
 var src = photosArray[4] + '?v=' + new Date().getTime(); // photosArray[5] doesn't exist because arrays are 0-indexed

 console.log('src: ' + src);

 function imageExists(src){
    var http = new XMLHttpRequest();
    var exists;

    http.open('GET', src, true);

    http.onreadystatechange = function(){
        if (http.readyState === 4 && http.status >= 200 && http.status < 300) {
            console.log('okay');
            exists = true;
            // do something
        }
        else {
            console.log('fail');
            exists = false;
            // do something
        }
        // do something, perhaps foo(exists);
    }

    http.send();

  }

  imageExists(src);
</script>

You can't simply return the value for exists from this function however, because XMLHttpRequest, as you noted, is asynchronous: imageExists() will return before the XMLHttpRequest has finished, and you'll get false negatives. Returning the event handler will not return a value for imageExists either, only for the event handler function. The best you can do is do something when the XMLHttpRequest's onreadystatechange (or, in modern browsers, onload) event fires.

You should look into Promises, the fetch API, or create a variable in the parent scope (updating it from the event handler) and check it at intervals, in order to ascertain whether the image exists.

Alternatively, use window.setTimeout with a variable in parent scope and check that variable after 'a reasonable amount of time' -- hint: judging what a reasonable amount of time should be is not a simple matter, given the almost impossible task of measuring the client's internet connection speed or deducing any kind of deterministically reliable estimate for 'a reasonable amount of time' for images of varying sizes.

Also, be aware that requesting an image may not give you a reliable result, as images can be cached by the browser. Hence why I append a cache-busting string to src with '?v=' + new Date().getTime();

What you can do is attach event handlers on the image itself, like this:

<img src="/path/to/whatever.jpg" onerror="this.style.display='none'">

or

<style>img.not-loaded { display: none; }</style>
<img src="/path/to/whatever.jpg" class="not-loaded" onload="this.className=''">

Either of which would serve to hide the image if it wasn't found or if the request for it otherwise failed.

Edit: based on your comments, this is how you might go about it:

window.imageExists = function(main_img){
    main_img.className+=' loaded';
    [].slice.call(main_img.parentNode.querySelectorAll('.secondary-image')).forEach(function(this_img){
      this_img.src = this_img.getAttribute('data-src');
    });
  };
  img { display: none; }
  img.loaded { display: block; width: 100%; }
  img.secondary-image.loaded { display: inline-block; width: 20%; }
  
<div class="main-image-container">
  <img src="http://i.imgur.com/VwQjLZI.jpg" class="main-image" onload="imageExists(this)">
  <img src="" data-src="http://i.imgur.com/VwQjLZI.jpg" class="secondary-image" onload="this.className+=' loaded';">
  <img src="" data-src="http://i.imgur.com/VwQjLZI.jpg" class="secondary-image" onload="this.className+=' loaded';">
  <img src="" data-src="http://google.com/does_not_exist.jpg" class="secondary-image" onload="this.className+=' loaded';">
  <img src="" data-src="http://i.imgur.com/VwQjLZI.jpg" class="secondary-image" onload="this.className+=' loaded';">
</div>
AM Douglas
  • 1,873
  • 1
  • 13
  • 17
  • I thought that's why you put the false flag in the http.open parameters, but I did set it to true because of that warning regarding, I have to read through your post some more. I know that 5th index doesn't exist, I did that on purpose because I wanted it to fail. – joehungjohn Oct 29 '16 at 03:41
  • The point of this is for a website I built to not display photo-containers if the client doesn't provide photos. Good point on the "cache-busting" part... I've seen some post using .on ready, one of them used new Image() which someone said would store photos un-necessarily in the browser, anyway thanks for the tips. I'll have to search around some more and try a different approach. Yeah promises is still something I have yet to learn, I came across the whole asynchronous/synchronous problem in another project. Did not go well. I also (irrelevant) dislike how you can't actually delete a script. – joehungjohn Oct 29 '16 at 03:44
  • 1
    Probably better to use an image tag and the onload event because you won't be subject to same origin limitations. – jfriend00 Oct 29 '16 at 03:47
  • @joehungjohn Yes, I just read your edit, I meant no offence. A synchronous XHR call would be devastating to performance as it would lock up the browser until the request was completed, which could take 2 seconds or 30 seconds, not ideal for the user. You could look into [web workers](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Synchronous_and_Asynchronous_Requests) but that would be overkill, I think. – AM Douglas Oct 29 '16 at 03:47
  • @jfriend00 I was just about to suggest that! Yes, what you could do is something like this: `` – AM Douglas Oct 29 '16 at 03:48
  • @amdouglas no offense taken. The whole asynchronous/synchronous thing is something I still have to master. Well you see I have four major photos, and each photo can have four sub-photos. So I wanted to run through a loop of each photo (whichever main photo is in focus) and see if any of the sub-photos are available, then display those, otherwise by default they're not shown. – joehungjohn Oct 29 '16 at 03:56
  • @amdouglas wow thanks a lot for your efforts. I have varying cases but ultimately what I'm after is to test if at least one of the photos exist, because there are other things that are displayed as well like a simple "title-container-div". Anyway, thanks for your help and I will see what I can use for my case. – joehungjohn Oct 29 '16 at 04:21
  • @amdouglas for sure. I think the basis of using onload will be what I'm after, updating a variable or something. – joehungjohn Oct 29 '16 at 04:24
1

Or use jQuery

$.post(src, {"photo": "1.jpg"}).done(function(data) {
    //Server returns 'true' or 'false'
    if(data) {
        console.log('okay');
    }
});
Akk
  • 406
  • 1
  • 5
  • 17
  • Why a post and not get? The link I provided also provided a jquery solution but used .get, I think the comment regarding asynchronous response is what I'm after ultimately. – joehungjohn Oct 29 '16 at 03:42
  • It's POST as I'm sending {"photo": "1.jpg"} object in the request. You can also use GET and add the photo name as a URL parameter. Depends how your route is configured. I suggested jQuery is because you wouldn't have to manually manage the asynchronous nature. The code will ONLY resolve once your server successfully returns the result. – Akk Oct 29 '16 at 03:50
  • Good point. I may have to find another route, I don't know... if your method works though, without struggle... ahhh can't think right now. Thanks for your time. – joehungjohn Oct 29 '16 at 03:54
  • I actually tried this out. Setting the source to 7.jpg for example (a source that doesn't exist) also returns "okay". I tried to see what data is, and it is the page itself when logged/alerted. I'm not sure if this is actually useful what you provided. Which is whatever, I have to figure out what I actually want to do. – joehungjohn Oct 29 '16 at 10:47