Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

call html2canvas on returned data from ajax #196

Closed
jsd219 opened this issue Apr 22, 2013 · 16 comments
Closed

call html2canvas on returned data from ajax #196

jsd219 opened this issue Apr 22, 2013 · 16 comments

Comments

@jsd219
Copy link

jsd219 commented Apr 22, 2013

is it possible to call html2cavas on the returned data from an ajax call. something like this?

$(function () {
    $.ajax({
           url: "PATH_TO_FILE",
           success: function(data) {
                      html2canvas(data, {
                        onrendered: function(canvas) {
                          document.body.appendChild(canvas);
                        }
                      });
                  },
           });
});
@jsd219
Copy link
Author

jsd219 commented Apr 23, 2013

anyone have any ideas on if this is possible?

@cobexer
Copy link
Contributor

cobexer commented Apr 24, 2013

did you test it? did something not work?

@jsd219
Copy link
Author

jsd219 commented Apr 24, 2013

yes, I tested it and can not get it to work.

@jsd219
Copy link
Author

jsd219 commented Apr 24, 2013

here is the error I get:
Uncaught TypeError: Cannot read property 'images' of undefined html2canvas.js:2184
_html2canvas.Preload html2canvas.js:2184
(anonymous function)

@felfert
Copy link
Contributor

felfert commented Apr 24, 2013

html2canvas renders the dom tree, so you must put the response data into the dom tree before you can let it render.
Look here for an example (no images in the example but in general that what you want): http://www.fritz-elfert.de/h2c

@jsd219
Copy link
Author

jsd219 commented Apr 24, 2013

Is the only difference between your code and this:

$(function () {
    $.ajax({
           url: "PATH_TO_EMAIL",
           success: function(data) {
                  $('#testapis').html(data);
                  },
           });
    setTimeout(function() {
      html2canvas($('#testapis'), {
        onrendered: function(canvas) {
          document.body.appendChild(canvas);
        }
      });
    }, 1000);
});

that you are sending the iframe off the page using absolute positioning? if so I really appreciate your help but I will have a page of 25 thumbnails. Everything about the code above works. you can view it here: http://www.pgiemaildev.com/test_html2canvas.html

I just want the second image to display (the thumbnail).

Is there a way to put the response into the dom using this method?

@felfert
Copy link
Contributor

felfert commented Apr 24, 2013

That's one. Keeping the element to be rendered visible (vs. display:none) helps the browser calculating the actual positions of it's content. Therefore, i shifted it out of the normal view.

The others:

A) Using an iframe instead of a div. Has 2 advantages:

  1. Keeps global css styles inside the frame not affecting the whole page (if I would render my black example pages into a div, the whole page would go black, because the loaded html includes a css stylesheet which contains body {background: black}. So: Not relying an the "chance" that the loaded content hopefully does not contain such things.
  2. An iframe does have a 'load' event which is much safer compared to the crude setTimeout workaround (where you have to guess the loading/rendering timing). Again, not only hoping that the chosen timeout is long enough but also starting much quicker at the earliest safe point in time.

and finally B): look at the frameDoc variable (used as argument to html2canvas): It is not the equivalent $('#testapis') but in fact 2 levels further down, because frames have additional levels in their dom hierarchy.
Using jquery in this particular case actually hides your problems instead of making them obvious.

"Cannot read property 'images' of undefined" means while trying to fetch the array of images (which every document has), html2canvas stumbles over an undefined thig which is supposed to be the document dom object. This can have several reasons: Either you specified the wrong root element in the first place (see my comment on jquery above) or you specified some element which is somehow not part of the page's regular dom hierarchy.

Why don't you simply take your debugger and step through the call of html2canvas - it should be pretty obvious then - otherwise it's just guesswork.

@felfert
Copy link
Contributor

felfert commented Apr 24, 2013

And to answer you actual first question of this thread: In your code above you are attempting to feed html2canvas a string (the ajax response) which is not a dom element, but a simple string of html markup.

@felfert
Copy link
Contributor

felfert commented Apr 24, 2013

And to answer your last question:

$('#testapis').html(data);

does just that. It puts the html markup contained in the string data into the $('#testapis') element, which in turn triggers the browser's intepretation/rendering cycle for that element. After that finished, is has produced the dom as a result which then can be fed into html2canvas.

@jsd219
Copy link
Author

jsd219 commented Apr 25, 2013

ok, first off I can't thank you enough for all your help. here is where I am at. I am almost there. :-)
below is my code:

$(document).ready(function() {
    $.ajax({
       url: '<?=HTTPROOT.'/preview.php?key='.$value['pte'];?>',
       success: function(data) {
              $('#testapis-<?=$value['id'];?>').html(data); //attr('padding-top','0px')
              },
       });
     setTimeout(function() {
     html2canvas([ document.getElementById('main') ], {
      onrendered: function(canvas) {
        var dataURL = canvas.toDataURL();

        $('#testapis-<?=$value['id'];?>').hide();
        $('.imageHolder-<?=$value['id'];?>').html('<img src="'+dataURL+'" style="width:200px;" />');
        $decoded = base64_decode(str_replace('data:image/png;base64,', '', dataURL));

      }
    });
    }, 1000);
});

Just above that code sits this bit of code:

<div class="imageHolder-'.$value['id'].'" align="center" style="padding-top:20%;color:#d1d1d1;">
Generating Preview<br><br><img src="'.HTTPROOT.'/images/ajax-cir-loader.gif">
</div>
<div id="testapis-'.$value['id'].'" style="visibility:collapse;"></div>';

All this code is inside a php foreach loop. It is creating endless loops and gives me various errors in the console. Can you see anything that is jumping out?

@felfert
Copy link
Contributor

felfert commented Apr 25, 2013

I'm not a PHP expert, but shouldn't it read **$**HTTPROOT instead of HTTPROOT ?

@felfert
Copy link
Contributor

felfert commented Apr 25, 2013

I think you lost me there ;). Where does that loop happen? In PHP? Or in the browser? Do you have an URL where I can see that?

@jsd219
Copy link
Author

jsd219 commented Apr 25, 2013

The HTTPROOT is correct. It is a constant not a variable. I currently have the javascript inside a php foreach loop. It was my desire to simply call the javascript function from the loop but I was having problems with it so for now I stuck it all within the loop. here is a link to a test page: http://www.pgiemaildev.com/includes/dashboard_TEST.php
I have stripped out a lot of the code for testing purposes.

@felfert
Copy link
Contributor

felfert commented Apr 26, 2013

OMG now I get it. Multiplying the whole javascript code in PHP is the completely wrong approach. No wonder you get tons of errors. In my sample code I do a loop as well, but on the client side in javascript. (It iterates over the array of urls). That's what you should do as well: In your PHP loop, you should generate the javascript code for an array definition. After that you output the javascript code once. In that javascript you loop over the array. Doing it that way, you also don't need multiple containers (in my example, I reuse a single iframe in every loop).

Something like this (PHP-side):

$urls = '';
foreach ($whatever as $value) {
    if ($urls != '') {
        $urls .= ",\n";
    }
    $urls .= "  '" . HTTPROOT . '/preview.php?key=' . $value['pte'] . "'";
}
echo "var pagesToShow = [\n" . $urls . "\n];\n";

This should result in the following JavaScript snippet:

var pagesToShow = [
  'http://whatever/preview.php?key=...',
  'http://whatever/preview.php?key=...',
  'http://whatever/preview.php?key=...'
];

After that, you can output my example code once almost verbatim.

@jsd219
Copy link
Author

jsd219 commented Apr 26, 2013

Thank you so much for all your help. I ended up deciding to go a different route this morning. I have figured out how to pull the images but I noticed it took a few seconds for html2canvas to process depending on the size of the page so displaying them in real time started to not look so good. I decided to save the images to a directory instead. It is working great.

Thank you so much, I could not have done this without your help.

God bless
jason

@feaswcy
Copy link

feaswcy commented Dec 7, 2018

use domContentLoaded or imgLoaded event works

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants