0

I am displaying some thumbnails which periodically get regenerated by an external process on the server. I want to display a spinner (gif) on the ones that are currently missing when the page loads, and replace the spinners with their thumbnails when they eventually arrive. Displaying a spinner is trivial. The thumbnail filenames are all known in advance. I haven't been able to figure out a way to watch for them to show up in Javascript.

It can be many minutes until some of the thumbnails get created. The process that creates them deletes them just before it does new processing which results in a new thumbnail. The interval between these external processing episodes can be hours.

Can anyone think of a way to do this? (no Ajax or Jquery - just basic Javascript).

Thanks.

EDIT - I'm sure this could be improved but it seems to work. Thanks for the hints and suggestions. It gave me the idea I was looking for. Someone with the ability to do so might want to remove the note at the top about this question already being answered with the link - that question is not relevant. --JKC

var intervalId, thumbs;

function refresh_thumbs() {
    // Refresh whether they need it or not. If the thumb isn't there,
    // onerror will (re)load the spinner. 
    for (i=0; i<thumbs.length; i++) {
        id = "snap_"+i;
        document.getElementById(id).src = thumbs[i];
    }
}

function init(thumbstring) {
    // Split comma-separated string into an array of thumbnail links
    thumbs=thumbstring.split(",");
    refresh_thumbs();
    intervalId = window.setInterval( refresh_thumbs, 5000); // milliseconds
}

As an example, one page might be generated containing the following:

<img src='/d/thumb_0.png' id='snap_0' onerror='this.src="/s/spinner.gif";'>
<img src='/d/thumb_1.png' id='snap_1' onerror='this.src="/s/spinner.gif";'>

<script>
    init("/d/thumb_0.png,/d/thumb_1.png");
</script> 

Calling refresh_thumbs before setInterval is necessary to prevent having to wait 5 seconds to see the thumbs which are there to begin with.

I have a suspicion I don't need the intervalId saved for any reason.

  • 1
    Welcome to StackOverflow. You need to show your code to get help. What have you tried? Are you getting an error? Help us help you. – htxryan Aug 29 '13 at 21:07
  • I got started with `code``code' which of course only works on the state at the time the page loads. It won't then change the spinner to the thumb, say, 2 minutes later when the thumb.png shows up – jkcunningham Aug 29 '13 at 21:14
  • I don't see how onload event binding is going to help here - an explanation would help more than a downvote. – jkcunningham Aug 29 '13 at 21:28
  • It's likely that you got the downvotes because you're not showing enough code. Showing code is a big deal in stackoverflow - we like helping people, but not doing all of the work for them. Also, your question is a little unclear. Without careful reading, it looks like `onload` would be helpful - try loading the image, set an event for when it loads, and you're good. What your question doesn't directly say is that since it takes so many hours, the load attempt times out, and then you're stuck. You'll get more positive results if you make this issue clear in your question. – Scott Mermelstein Aug 29 '13 at 22:17
  • @jkcunningham If you have an answer, it's perfectly acceptable - and preferable to answering within the question - for you to answer your own question, and once you do, mark your answer as the accepted answer. (You may have to wait a day more to accept it; I'm not sure.) That way others who stop by this question will see a clear answer. The message you're seeing about the question already being answered, if it's on top of everything else, is currently only visible to you. If you're talking about the comment from Jacob Krall, don't worry about it. Again, it may help a future visitor. – Scott Mermelstein Aug 30 '13 at 16:39
  • @jkcunningham With your current setup, you don't need to store `intervalId`. You only need that if you intend to clear the interval. Of course, right now, you're constantly loading all thumbnails every five seconds, which is not highly efficient. You may eventually want to have a counter of images you're trying to load, and on either load or error, store a value that says how many succeeded. If they all succeed, you could stop or slow your interval. – Scott Mermelstein Aug 30 '13 at 16:41

2 Answers2

1

Use setInterval to periodically change the src of the images that are waiting. You can append a query string to the image URL in the form of a timestamp to prevent caching. Not sure how you detect if an image is ready or not this way, but I'm sure that's easy to figure out with some trial and error.

Update: onerror triggers for each unreachable URL you set. Keep polling URLs with new timestamps until you stop receiving errors (that means the image is ready).

Update: I played around with this problem trying to find a general solution. Here's what I came up with:

HTML:

<body style="background-color: #CCC;">
    <img src="/img/fail.png" width=200 onerror="imgErr(this);" />
    <img src="/img/fail.png" width=200 onerror="imgErr(this);" />
    <img src="/img/fail.png" width=200 onerror="imgErr(this);" />
    <img src="/img/fail.png" width=200 onerror="imgErr(this);" />
    <img src="/img/fail.png" width=200 onerror="imgErr(this);" />
    <img src="/img/fail.png" width=200 onerror="imgErr(this);" />
</body>

JS:

var imgElems = [],
    imgUrls = [];

function imgErr(img) {
    imgElems.push(img);
    //imgUrls.push(img.src); // <-- real code
    imgUrls.push('/img/logo.png'); // <-- literal url for testing
}

onload = function () {
    var interval,
        timeout = 3000,
        time,
        url,
        img;

    interval = setInterval(function () {
        time = new Date().getTime();
        while (imgElems.length) {
            img = imgElems.shift();
            url = imgUrls.shift();
            img.src = url + '?time=' + time;
        }
    }, timeout);
}

JSFiddle

Jo Are By
  • 3,293
  • 1
  • 11
  • 11
0

If I'm understanding correctly, your issue isn't knowing when the image loads; your issue is that you can't issue a request for the image because it may take hours to load. So when you set the src, it times out, and that's all the information you get.

You have a few choices.

Perhaps the easiest way to get the file is continuously attempt to reload each image. In that case, you're close, though you'll want a separate div with the spinner to show while waiting:

<img src='spinner.gif' id='spinner' />
<img src='thumb.png' id='thumb' />

document.getElementById("thumb").onload = function() {
    document.getElementById("spinner").style[display] = "hidden";
}
document.getElementById("thumb").onerror = function() {
    this.src = "thumb.png";
}

You'd need to tweak the positioning, but this will make a spinner, make it disappear when the image loads, and attempt to reload it when the image is not yet ready.

The other alternative I was going to suggest has already been suggested by Jo Are By - set an interval every few minutes to reset the source of the images. There are lots of variations you can do here (reset all sources, or only failed ones, e.g.), but basically, at the end of your file, you'll want something like this:

var intervalId = window.setInterval( function() {
    var elem = document.getElementById("thumb");
    // set the source twice, since I dont' see anywhere in the spec if setting 
    // the src to itself forces a reload.  
    var src = elem.src;
    elem.src = "http://foofoofoofoofoo.bar.bar";
    elem.src = src;
}, 300000); // set to every 5 minutes (300 seconds * 1000 milliseconds)

Somewhere in there, you'll need to keep track of how many are still pending, and when it hits 0, call window.clearInterval(intervalId);.

Scott Mermelstein
  • 15,174
  • 4
  • 48
  • 76
  • var check_again = []; function init(thumbstring) { thumblist=thumbstring.split(","); for (i=0; i – jkcunningham Aug 30 '13 at 04:01
  • I'm having trouble with the markdown. I entered the above with four leading spaces each line and that's what I got. And the character limit seems really minimal to actually talk about this stuff. @scott how did you enter a comment with code that long? – jkcunningham Aug 30 '13 at 04:09
  • @jkcunningham I didn't enter a comment; I entered an answer. Code (with 4 spaces per line) is unlimited in questions and answers. You can edit your question to add the code you added as a comment. The comment I entered on your question has a limit of 600 characters. You can highlight things, typically code, using backquote. (Right below ESC on American keyboards.) Once you have enough reputation, you can edit others' questions and answers as well. – Scott Mermelstein Aug 30 '13 at 05:43
  • `elem.src = src + '?time=' + new Date().getTime()` will likely force a reload and prevent caching. – Jo Are By Aug 30 '13 at 09:39
  • @JoAreBy That will definitely force a reload, but I wasn't certain his server would handle it properly. I'm not sure that caching is an issue - if the file is deleted by the server, won't it come back with a 404, and ignore any cached file it has? As far as I know, you still have to return a checksum of the file for caching, and once the file is removed, the checksums won't work. – Scott Mermelstein Aug 30 '13 at 14:28
  • @JoAreBy Thank you for the explanation. That should be sitting in a box at the top of the help page on Markdown. – jkcunningham Aug 30 '13 at 14:35
  • 1
    @jkcunningham I think you meant `@ScottMermelstein`. ScottMermelstein, I was thinking of browser cache, which I guess is irrelevant in this case. – Jo Are By Aug 30 '13 at 16:14
  • You are right - it was @ScottMermelstein I meant. Sorry about that. Still finding my way around this forum. As far as the cache goes, you (both) may be right - some browser caches may cause trouble when I try to reload the same name after it's been the spinner. Scott's approach is probably the way to go in that case. So far with Firefox it hasn't been a problem, but I haven't tried it with Chrome or IE-anything yet. – jkcunningham Aug 30 '13 at 22:47