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

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

Closed
ghost opened this issue Feb 22, 2012 · 36 comments
Closed

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

ghost opened this issue Feb 22, 2012 · 36 comments

Comments

@ghost
Copy link

ghost commented Feb 22, 2012

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...

@niklasvh
Copy link
Owner

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.

@ghost
Copy link
Author

ghost commented Feb 23, 2012

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

@ghost
Copy link
Author

ghost commented Feb 23, 2012

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...

@usmonster
Copy link
Contributor

@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.

@brcontainer
Copy link
Contributor

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)],...)

@usmonster
Copy link
Contributor

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.)

@brcontainer
Copy link
Contributor

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>

@usmonster
Copy link
Contributor

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.

@brcontainer
Copy link
Contributor

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);
});

@usmonster
Copy link
Contributor

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.

@usmonster
Copy link
Contributor

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

@ejlocop
Copy link

ejlocop commented Apr 30, 2015

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

@usmonster
Copy link
Contributor

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

@Alebqto
Copy link

Alebqto commented Jan 17, 2016

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

@pkpatels
Copy link

it's not working.

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

@uriklar
Copy link

uriklar commented Mar 16, 2016

@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

@pkpatels
Copy link

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

@pkpatels
Copy link

@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

@uriklar
Copy link

uriklar commented Mar 16, 2016

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

@ddykhoff
Copy link

ddykhoff commented Apr 29, 2016

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 */}
);

@alana314
Copy link

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.

@xzw123
Copy link

xzw123 commented May 30, 2016

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)

@vance
Copy link

vance commented Jun 22, 2016

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.

@vance
Copy link

vance commented Jun 22, 2016

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.

@vance
Copy link

vance commented Jun 22, 2016

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.

@arjunasuresh3
Copy link

+1

@simonmysun

This comment has been minimized.

@KingGeneral
Copy link

KingGeneral commented Dec 13, 2016

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

@KingGeneral
Copy link

KingGeneral commented Dec 13, 2016

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.

@wangxdmm
Copy link

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

@ruthraprakash
Copy link

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.

@voracity
Copy link

@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);
		});
	});
}

@zakapong
Copy link

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');
  });


}

`

@shasu01
Copy link

shasu01 commented Jun 15, 2020

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

@nishantgupta11
Copy link

  -> Add CSS **overflow-y: scroll**; to HTML element **pdfHTML**
  -> Add attribute **width** & **windowWidth** to html2canvas method, which should be equal to width of **pdfHTML** Div
  
   
    html2canvas(document.querySelector(".pdfHTML"), {
        width: 1350, windowWidth: 1350
    }).then(canvas => {
        base64stringpdf = canvas.toDataURL("image/jpeg");
    });

@nishantgupta11
Copy link

-> Add CSS overflow-y: scroll; to HTML element pdfHTML
-> Add attribute width & windowWidth to html2canvas method, which should be equal to width of pdfHTML Div

html2canvas(document.querySelector(".pdfHTML"), {
    width: 1350, windowWidth: 1350
}).then(canvas => {
    base64stringpdf = canvas.toDataURL("image/jpeg");
});

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