2

I have a web page which contains an iframe, the iframe contains a table which contains up to 1,000 canvas elements. The canvas elements are filled by a function defined in the iframe, which is called by the parent page. The function iterates and fills each canvas with a base64 data URI contained in a hidden span in the parent page. I have read a few other Stackoverflow posts regarding similar issues but I have already tried the most commonly used suggestion (using onload) and that does not seem to work. I am fairly fresh to html/javascript so I doubt I am doing things the best way. I was hoping someone could guide me on how to assure that all the canvas elements are successfully filled with the base64 data on the first load so I don't need to refresh multiple times to get me thumbnails? Here is some code, it is VERY trimmed up code from my html page. If I were to paste the entire page, it might be a bit too much too handle sensibly. Here is what I believe to be the essential actors.

My onload call to my function which fills the canvases, this is on the main page which contains the iframe -

<body onload="document.getElementById('framePage').contentWindow.fillCans();">

Here is the fillCans() function which is contained within the framePage iframe (I probably should move this to the main html page, but so far this is how the script has... evolved haha). pgCnt element contains the "counter" numbers if you will. It will defines how many elements to iterate through. When var c is assigned, the "can"+i elements are the canvases. When var imgSrc is assigned, the "span"+i elements are the hidden span elements on the parent page which contain the base 64 data URIs -

function fillCans() {
    var cnt = document.getElementById("pgCnt").innerHTML;
    var cnt = cnt.split("-");
    var cnt1 = cnt[0];
    var cnt2 = cnt[1];
    for (var i=cnt1; i <= cnt2; i++) {
        var targ = "span"+i
        var c = document.getElementById("can"+i);
        var ctx = c.getContext("2d");
        ctx.fillStyle="#FFFFFF";
        ctx.fillRect(0,0,128,128);
        var image = new Image();
        var imgSrc = parent.document.getElementById("span"+i).innerHTML;
        imgSrc = imgSrc.split("*");
        image.src = imgSrc[0];
        ctx.drawImage(image,0,0);
    }

Like I said, this will load fine, firebug reports no errors, however all the canvases are white with no thumbnails. When I hit refresh a few times they all finally load. I've read that the image data is asynchronously loaded but I am not certain to as what that means, I assume it means it doesn't play by the rules that onload plays by? Anyways as usual, thanks for all the help stackoverflow community. It will be great to know how to get these thumbnails to load successfully on the first page load.

After @Sebastian's great suggestions I was able to get my JavaScript functioning as it should. I am pasting in the new function. As it works now, in case anyone ever comes across a similar issue. I had to implement an onload call to a function which drew the image, and then utilize in IIFE for loop to insure that each thumbnail was drawn with correctly iterated data.

function fillCans() {
var cnt = document.getElementById("pgCnt").innerHTML;
var cnt = cnt.split("-");
var cnt1 = cnt[0];
var cnt2 = cnt[1];
for (var i=cnt1; i <= cnt2; i++) {
    (function(iterable){
        var c = document.getElementById("can"+iterable);
        var ctx = c.getContext("2d");
        ctx.fillStyle="#FFFFFF";
        ctx.fillRect(0,0,128,128);
        var image = new Image();
        var imgSrc = parent.document.getElementById("span"+iterable).innerHTML;
        imgSrc = imgSrc.split("*");

        image.onload = function() {
            ctx.drawImage(image, 0, 0);
        };
        image.src = imgSrc[0];
    })(i);
}
}
0xhughes
  • 2,703
  • 4
  • 26
  • 38
  • please...can you reduce the content of your question?? – HIRA THAKUR Aug 02 '13 at 14:11
  • @MESSIAH Sorry MESSIAH, I feel that if I left out some information that my question might not be answerable, as I was not sure at all where to begin to look in troubleshooting my code. – 0xhughes Aug 02 '13 at 20:08

1 Answers1

3

You are right, the problem is that the image has to be loaded first, before you can draw it. In your case image is not loaded at the time you call

ctx.drawImage(image,0,0);

However later on the browser has cached the image URL and in between the calls to the assignment of the source property and the drawImage call the browser has already retrieved the image from the cache.

To make it work in any case, you need to wait for the image to load:

  image.onload = function() {
    ctx.drawImage(image, 0, 0);
  };
  image.src = imgSrc[0];

Be sure to first register the callback and then set the src.

See HTML Canvas DrawImage Tutorial

Sebastian
  • 7,729
  • 2
  • 37
  • 69
  • Thank you so much for the information @Sebastian. I ran the modified script, with the two lines edited in the correct order... it kind of works! In my test set I have 7 images which appear in the table. Well only the last image appears, but it does on the correct (first) load of the page! However when I hit refresh none of the other images load. I have a feeling this must be something to do with the callback function (did I refer to that correctly?) residing in my iterating function. – 0xhughes Aug 02 '13 at 18:42
  • After doing a bit more research I think my new issue is more inline with closures and scoping and whatnot. However, the image did successfully load on first page load, as it was intended to, now I just need to figure out the next bug. Thanks for the help again! – 0xhughes Aug 02 '13 at 20:08
  • @0xhughes: The code won't work in a loop, since `image` will have the last value assigned when the callback executes (all use the same closure variable). To workaround this use an "IIFE" as your loop body and you get a fresh variable for each invocation so that all images will load correctly. – Sebastian Aug 02 '13 at 20:25
  • Thanks @Sebastian. Good mental refresher – welbornio Mar 24 '15 at 16:28