Html2canvas: How to render full of hidden part in 'overflow:auto' div

Created on 22 Feb 2012  ·  34Comments  ·  Source: niklasvh/html2canvas

I have a div which contains all the elements that have to be converted into an image. And that div is set to 'overflow:auto'. I want to know how I can export all the elements(even hidden in the scrolling) to an image. I appreciate and welcome all your comments and answers...... Thanks in advance...

Most helpful comment

@ruthraprakash, I had a similar issue. This works for my simple purposes on the latest version:

function fullhtml2canvas(el) {
    return new Promise(resolve => {
        html2canvas(el, {
            width: el.scrollWidth,
            height: el.scrollHeight,
        }).then(canvas => {
            resolve(canvas);
        });
    });
}

All 34 comments

There currently isn't an option to render the page with specific options, so you'll need to modify the page to look the way you want it to render.

In other words, if you remove the height/width limit, run html2canvas, and then set the height/width back to the value it was, you should achieve what you are looking for.

Thank you very much.... I shall try what you said...

I tried for a long time... But it makes a lot of problem... I could not achieve what you said. I found out that it works well when it is 'body' even if there is scrolling... I want to achieve the same for a div which has elements with scrolling... If you could help me out with this issue it would be of great help for me... I am looking forward to your comments...

@niklasvh, I am having the same issue, and the only workarounds I've are to either shrink the width of the target element before the call to html2canvas (not always an option), or to call html2canvas twice in a row on the same element. Here's a fiddle to demonstrate the issue:

http://jsfiddle.net/fMeRC/1/ (this particular example works just for Chrome; you have to fiddle with the width to see the issue in other browsers)

This basically only happens when the element is wider than the window or (document.width - 8), I'm not sure which has the more important/direct relevance, and this may change on other browsers).

Probably related: #199 and #108.

Tried childElement or set "overflow=visible" and after set "overflow=auto|hidden|inherit" ?

childElement without style, only to receive the contents of the div with overflow:

<div id="main"><!--// main div -->
<div class="mainContent"><!--// div without style (height: auto, width: auto, margin: 0, padding: 0) -->
...content...
html2canvas([$("#main div.mainContent").get(0)],...)

Thanks for the suggestions, @brcontainer. I had tried the overflow: visible -> html2canvas( ... ) -> overflow: [old overflow] approach, though the flash of the hidden/unhidden/hidden-again content was too noticeable (possibly because my target element is relatively complex). Though I just came up with a slightly optimized variant of the workaround that seems to make a difference in that respect:

html2canvas( targetElement, {
  onpreloaded: function(){ /* set parent overflow to visible */},
  onparsed: function(){  /* reset parent overflow */ },
  // ... onrendered, etc.
});

This sets the overflow to 'visible' only for (what I think is) the shortest amount of time necessary for h2c to properly parse the target, thus eliminating the flash almost entirely, at least in my case. That is, we no longer have to wait for the canvas rendering step to finish before resetting/re-hiding the overflow. I have no idea if this modified approach will work for everyone, but I do think it's a nicer workaround than calling html2canvas( ... ) twice in a row or adding extra stuff to the DOM to wrap the target content.

In any case, this is still a real bug that I'd like to see properly fixed. (Especially since neither workaround seems to work correctly for me in IE9&10, at least--haven't tried in IE11.)

_Note: The aforementioned workaround would not work with the newest version that is currently in master. I haven't tested, but it looks like you would need to make use of the oncloned option instead, and the overflow parent reset would now be called in your callback to .then(). (It should also be possible to exclusively make the parent's overflow visible inside the cloned document and not worry about resetting anything at all.)_

I do not understand why use this way you did, it would be better like this:

function SnapShotDOM(target,call){
    var data = {};

    //use jquery or getComputedStyle|style.overflow
    data.overflow = /*get overflow property*/;
    data.width = /*get width property*/;
    data.height = /*get height property*/;
    data.maxWidth = /*get maxWidth property*/;
    data.maxHeight = /*get maxHeight  property*/;

    target.style.overflow="visible !important";
    target.style.height="auto !important";
    target.style.maxHeight="auto !important";

    html2canvas(target, {
        "onrendered": function(canvas) {
            target.style.overflow = data.overflow;
            target.style.width = data.width;
            target.style.height = data.height;
            target.style.maxWidth = data.maxWidth;
            target.style.maxHeight = data.maxHeight;
            call(canvas);
        }
    });
}

SnapShotDOM(document.body,function(canvas){
    console.log(canvas);
});

or use class:

<style>
*.html2canvasreset{
    overflow: visible !important;
    width: auto !important;
    height: auto !important;
    max-height: auto !important;
}
</style>
<script>
function SnapShotDOM(target,call){
    var data = target.className;
    target.className += " html2canvasreset";//set className - Jquery: $(target).addClass("html2canvasreset");
    html2canvas(target, {
        "onrendered": function(canvas) {
            target.className = data;//old className - Jquery: $(target).removeClass("html2canvasreset");
            call(canvas);
        }
    });
}

SnapShotDOM(document.body,function(canvas){
    console.log(canvas);
});
</script>

The reason I don't put anything related to overflow in onrendered is because the time between parsing and rendering can be noticeable, and resetting the overflow as soon as possible (i.e., right after parsing, before rendering) is preferable to avoid a visible flash of content. Putting the overflow reset in onparsed instead accomplishes this.

I was referring to "onpreloaded".

try this:

function SnapShotDOM(target,call){
    var data = target.className;
    target.className += " html2canvasreset";//set className - Jquery: $(target).addClass("html2canvasreset");
    html2canvas(target, {
        "onparsed": function() {
            target.className = data;//old className - Jquery: $(target).removeClass("html2canvasreset");
        },
        "onrendered": function(canvas) {
            call(canvas);
        }
    });
}

SnapShotDOM(document.body,function(canvas){
    console.log(canvas);
});

My use of onpreloaded is for the same reason--it's an optimization that allows me to move the overflow un-hide part to the latest possible moment, just before the parsing step, thus minimizing the amount of time for which the content is completely visible on the screen. You can see how this works in the code here and here.

@niklasvh, is this issue at all related to the TODO found here (options.useOverflow)?

what version did you guys used?.. i can find onparsed and onpreloaded method in my version..
im using html2canvas-0.5.0-alpha1

@ejlocop This issue was filed years ago for a previous version and may not be relevant for your use case (#511).

They solved the problem? paraa solution I 'm looking for something with my Gantt chart , I need you out completely ...
overflow

it's not working.

http://jsfiddle.net/fMeRC/368/

@pkpatels Why not working? I ran your fiddle and the sentences were rendered without cutting off...
I also had this issue and the solution the worked for me is the one suggested in: #117

@uriklar It's working for horizontal scroll not for vertical. Checkout my div. There are three different elements there.

@uriklar the solution you suggest is also not working when i scroll down the div and click on totally visible.
screen shot 2016-03-16 at 1 41 09 pm

What about setting the height of #text div to auto before render and resetting it after? That will work.

I was still having issues with overflow in the most recent release (0.5.0-beta4). I took @usmonster's workaround and adapted it to the recent changes to h2c and Promises (as described in @usmonster's note):

Note: The aforementioned workaround would not work with the newest version that is currently in master. I haven't tested, but it looks like you would need to make use of the oncloned option instead, and the overflow parent reset would now be called in your callback to .then(). (It should also be possible to exclusively make the parent's overflow visible inside the cloned document and not worry about resetting anything at all.)

So without altering the actual DOM of your webpage, you can alter the cloned document used for rendering by h2c utilizing the onclone callback. It's pretty simple actually, here's my h2c call:

var renderingPromise = html2canvas($(item), {
    width: item.clientWidth,
    height: item.clientHeight,
    onclone: function(clone) {
            $(clone).find('.grid-stack-item-content')
                .css('overflow-x', 'auto')
                .css('overflow-y', 'auto');
        return true;
    }
}).then(
    function(c) {/* success */},
    function() {/* fail */}
);

The only thing that worked for me was putting the canvas in an iframe, calling html2canvas inside that, calling toDataURL() on the canvas, and then passing that image back to the top window using a callback. I suppose you could also pass back a canvas object.

return renderDocument(node.ownerDocument, options, node.ownerDocument.defaultView.innerWidth, node.ownerDocument.defaultView.innerHeight, index).then(function(canvas) { if (typeof(options.onrendered) === "function") { log("options.onrendered is deprecated, html2canvas returns a Promise containing the canvas"); options.onrendered(canvas); } return canvas; });
change to
var width = options.width != null ? options.width : node.ownerDocument.defaultView.innerWidth; var height = options.height != null ? options.height+node.ownerDocument.defaultView.innerHeight : node.ownerDocument.defaultView.innerHeight; return renderDocument(node.ownerDocument, options, width, height, index).then(function (canvas) { if (typeof(options.onrendered) === "function") { log("options.onrendered is deprecated, html2canvas returns a Promise containing the canvas"); options.onrendered(canvas); } return canvas; });

This can solute the question about overflow of screen(both width and height)

xzw123 has a bad solution: setting ownerDocument.defaultView.innerHeight manually causes it to unbind from actual window sizing events. I think it would be best to do this in an iframe. I'm keen on trying Jordan's solution.

Jordan, how did you end up using canvas in an iframe? I'm attempting it now. I think this should be the final solution in html2canvas... the iframe communication should be made invisible external to the API.

Ah yes, doing it in an iframe with window.postMessage(myData, ... ) does the trick. You can post back the whole data url of the rendered pixels. I just put my rendering code in the HTML of the iframe, passed in the data needed to render the HTML source, have it render to canvas, then have it spit the toDataUrl data back out.

+1

Hi. After searching for solutions and observing how the code run, I write my own tricky solution.

https://gist.github.com/simonmysun/c6da13ce2827a374e71ccf07d9f08e2d

It work for me and I hope it will help someone who needs.

However, I still hope it can be supported in this library in the future.

i still not get it, but for my issue is the image get cut off. when using chrome Version 54.0.2840.99 m
(half image got screenshotted, but the other half got black screen)

// the timeline show up 100%, but cut off, cause its static and not dynamic.
// (its cut off from the screen,but the size, and picture show as normal, 100% show)
// after moving to the canvas, everthing that not in the screen will get blank in chrome  
// it depends to the user screen, if i zoom out my chrome, i get 100% image.

but when using Mozilla 50.0.2, its working fine without zoom out. (100% image),

i dont know why.

code :

var zenleft = $("#leftPanel").width();  //calculate left side
var zenright = $(".dataPanel").width();  //calculate right side
var after = zenright + zenleft + 250;  // add all

//add the css, even after add overflow still not working
$("#timelineTarget").css({"background-color":"white","width":after});   

 html2canvas(canvTimeline, {
    onrendered: function (canvas) {
            $("#previewImage").html(canvas);
            getCanvas = canvas;
   }
});

image

Finally i solve this issue by adding

$("body").css({"width": "+="+after}); //after is a total of the width of the element i want to view

so

var zenleft = $("#leftPanel").width();  //calculate left side
var zenright = $(".dataPanel").width();  //calculate right side
var after = zenright + zenleft + 250;  // add all
$("body").css({"width": "+="+after});  //add the body

//add the css
$("#timelineTarget").css({"background-color":"white","width":after});   

 html2canvas(canvTimeline, {
    onrendered: function (canvas) {
            $("#previewImage").html(canvas);
            $("#timelineTarget").css({"background-color":"white","width":old});   //turn it back
            $("body").css({"width": "-="+after});   //turn it back
            getCanvas = canvas;
   }
});

hope it solve your own issue.

onclone maybe be the correct method. You can change css to get what you want....

Hi All, I have div which is scrollable. When i send the div input as canvas parameter, it just rendering what is avialable in viewport. I gone through all solutions. I didnt understand. Can anyone tell how to take entire content in div as screenshot.

@ruthraprakash, I had a similar issue. This works for my simple purposes on the latest version:

function fullhtml2canvas(el) {
    return new Promise(resolve => {
        html2canvas(el, {
            width: el.scrollWidth,
            height: el.scrollHeight,
        }).then(canvas => {
            resolve(canvas);
        });
    });
}

I have an issue with taking a screenshot of the timeline chart. I am not geeting the x axis value. it is just creating the image of the timline chart.
` demoFromHTML(e) {
e.preventDefault();

  let input = window.document.getElementById('divToPDF');


  html2canvas(input)
  .then((canvas) => {

    const imgData = canvas.toDataURL('image/jpeg');

    const pdf = new pdfConverter("l", "pt");
    pdf.addImage(imgData, 'JPEG', 15, 110, 800, 250);
    pdf.save('test.pdf');
  });


}

`

I tried for a long time... But it makes a lot of problem... I could not achieve what you said. I found out that it works well when it is 'body' even if there is scrolling... I want to achieve the same for a div which has elements with scrolling... If you could help me out with this issue it would be of great help for me... I am looking forward to your comments...

Please can you show me how you got body to work

Was this page helpful?
0 / 5 - 0 ratings