Targeting a specific div with html2canvas - javascript

I'm using Hertzen's html2canvas.js, and tried to adjust the example code so that it targets a specific div instead of the entire body of a document:
html2canvas(document.body, {
onrendered: function(canvas) {
document.body.appendChild(canvas);
}
});
A sample of what I'm trying to accomplish is here: https://jsfiddle.net/ununkg3a/
On clicking the "Save PNG" button, I want to append an image of the jQuery generated squares that I'm targeting in a specific div. In the code, it appears that it's appending something, but it doesn't have a height. When I try to set a height with the output, it still doesn't work as expected.
Is it not possible to accomplish something like this? Whenever I change document.body to another element, the screenshot doesn't render anymore, although it does render when I change it back to document.body. Someone told me that I'd have to crop the image with js, but that seems a little hacky.

It can: it's the first attribute.
(fiddle)
Example:
html2canvas(document.getElementById('test')).then(function(canvas) {
document.body.appendChild(canvas);
});
In your example, canvas2html can't find out the height of your div. Because of that it falls back to 0px height, so you won't see anything.
Give width to the #art then you can count the height manually and use that.
Mathematic example:
art_square_width = 10px
art_square_height = 10px
art_square_amount = 500
art_width = 250px
art_height = art_width / (art_width / art_square_width) * art_square_height = 200px
(fiddle)

Related

Text Not Ellipsis at certain point using jQuery Plugin

I currently have a 'widget' div which has a static height, within it is an image which also has a static height. The only thing that can have a dynamic height is the title which can change from 1-3 lines long.
What's happening is that I'm trying to make the description within the div (which can be quite long) ellipsis before the containing div ends, taking into account the title which can vary in height.
I'm using a jQuery plugin called dotdotdot which docs can be found here http://dotdotdot.frebsite.nl/
The plugin is working but I think my JS might be off a bit. Would love some help as I just can't get my brain around it.
Fiddle Here
You can see it clearly on the fiddle but JS below.
$(document).ready(function () {
$(".caption").each(function () {
var authorheight = $('.meta').height();
var h2height = $('h4').height();
$(".desc").height(250 - h2height - authorheight);
$(".desc").dotdotdot({
after: "a.readmore"
});
});
});
Any help would be brilliant!
Thanks
You were doing everything right except for calculating the Height.
var authorheight = $('.meta').innerHeight();
var h2height = $('h4').innerHeight();
the above help you get the height along with the padding and everything.
Then next id you left padding which you have applied to .caption so your
height for .desc becomes as below
$(".desc").height(250 - h2height - authorheight -40);
UpdatedFiddle

How do I get the height of a textarea

I need to get the height of a textarea. Seemingly so simple but it's driving me mad.
I have been researching for ages on stackoverflow with no luck: textarea-value-height and jquery-js-get-the-scrollbar-height-of-an-textarea and javascript-how-to-get-the-height-of-text-inside-of-a-textarea, among many others.
This is how it looks currently:
This is how I want it to look, open a full height:
.
Here is my html:
<textarea id="history" class="input-xxlarge" placeholder="Enter the content ..." rows="13"></textarea>
CSS:
.input-xxlarge {
display: inline-block;
height: auto;
font-size: 12px;
width: 530px;
resize: none;
overflow: auto;
}
jQuery:
var textarea = $('#history');
I've tried (inter alia):
1. textarea.height() --> always returns 0
2. textarea.ready(function() { // wait for DOM to load
textarea.height();
}
3. getting scrollheight from textarea as an HTMLTextareaElement (i.e. DOM Element) --> returns 0
4. var contentSpan = textarea.wrapInner('<span>');
var height = contentSpan.height(); --> always returns 0
Please help, I'm at my wit's end!
Ok, I've found a solution. Whether it's the best solution, I don't know, but it works and that, frankly, is all I care about, having spent almost a day on this issue.
Here it is for anyone who faces the same problem:
Select the textarea:
var textarea = $('#history');
Get the textarea's text:
var text = textarea.text();
Create a temporary div:
var div = $('<div id="temp"></div>');
Set the temp div's width to be the same as the textarea. Very important else the text will be all on one line in the new temp div!:
div.css({
"width":"530px"
});
Insert the text into the new temp div:
div.text(text);
Append it to the DOM:
$('body').append(div);
Get the height of the div:
var divHeight = $('#temp').height();
Remove the temp div from the DOM:
div.remove();
Had a similar issue, in my case I wanted to have an expand button, that would toggle between two states (expanded/collapsed). After searching also for hours I finally came up with this solution:
Use the .prop to get the content height - works with dynamically filled textareas and then on a load command set it to your textarea.
Get the inner height:
var innerHeight = $('#MyTextarea').prop('scrollHeight');
Set it to your element
$('#MyTextarea').height(innerHeight);
Complete code with my expand button(I had min-height set on my textarea):
$(document).on("click", '.expand-textarea', function () {
$(this).toggleClass('Expanded');
if($(this).hasClass('Expanded'))
$($(this).data('target')).height(1);
else
$($(this).data('target')).height($($(this).data('target')).prop('scrollHeight'));
});
Modern answer: textarea sizing is a few lines of ES6 implementable two primary ways. It does not require (or benefit from) jQuery, nor does it require duplication of the content being sized.
As this is most often required to implement the functionality of auto-sizing, the code given below implements this feature. If your modal dialog containing the text area is not artificially constrained, but can adapt to the inner content size, this can be a perfect solution. E.g. don't specify the modal body's height and remove overflow-y directives. (Then no JS will be required to adjust the modal height at all.)
See the final section for additional details if you really, truly only actually need to fetch the height, not adapt the height of the textarea itself.
Line–Based
Pro: almost trivial. Pro: exploits existing user-agent behavior which does the heavy lifting (font metric calculations) for you. Con: impossible to animate. Con: extended to support constraints as per my codepen used to explore this problem, constraints are encoded into the HTML, not part of the CSS, as data attributes.
/* Lines must not wrap using this technique. */
textarea { overflow-x: auto; white-space: nowrap; resize: none }
for ( let elem of document.getElementsByTagName('textarea') ) {
// Prevent "jagged flashes" as lines are added.
elem.addEventListener('keydown', e => if ( e.which === 13 ) e.target.rows = e.target.rows + 1)
// React to the finalization of keyboard entry.
elem.addEventListener('keyup', e => e.target.rows = (elem.value.match(/\n/g) || "").length + 1)
}
Scrollable Region–Based
Pro: still almost trivial. Pro: animatable in CSS (i.e. using transition), though with some mild difficulty relating to collapsing back down. Pro: constraints defined in CSS through min-height and max-height. Con: unless carefully calculated, constraints may crop lines.
for ( let elem of document.getElementsByTagName('textarea') )
elem.addEventListener('keyup', e => {
e.target.style.height = 0 // SEE NOTE
e.target.style.height = e.target.scrollHeight + 'px'
})
A shocking percentage of the search results utilizing scrollHeight never consider the case of reducing size; for details, see below. Or they utilize events "in the wrong order" resulting in an apparent delay between entry and update, e.g. pressing enter… then any other key in order to update. Example.
Solution to Initial Question
The initial question specifically related to fetching the height of a textarea. The second approach to auto-sizing, there, demonstrates the solution to that specific question in relation to the actual content. scrollHeight contains the height of the element regardless of constraint, e.g. its inner content size.
Note: scrollHeight is technically the Math.max() of the element's outer height or the inner height, whichever is larger. Thus the initial assignment of zero height. Without this, the textarea would expand, but never collapse. Initial assignment of zero ensures you retrieve the actual inner content height. For sampling without alteration, remove the height override (assign '') or preserve (prior to) then restore after retrieval of scrolllHeight.
To calculate just the height of the element as-is, utilize getComputedStyle and parse the result:
parseInt(getComputedStyle(elem).height, 10)
But really, please consider just adjusting the CSS to permit the modal to expand naturally rather than involving JavaScript at all.
Place this BEFORE any HTML elements.
<script src="/path/to/jquery.js"></script>
<script>
$(document).ready(function(){
var textarea = $('#history');
alert(textarea.height()); //returns correct height
});
</script>
You obviously do not have to alert it. I was just using an easily visible example.
Given a textarea with an id of "history", this jQuery will return it's height:
$('#history').height()
Please see a working example at http://jsfiddle.net/jhfrench/JcGGR/
You can also retrieve the height in pixels by using $('#history').css('height'); if you're not planning on doing any calculations.
for current height in px:
height = window.getComputedStyle(document.querySelector('textarea')).getPropertyValue('height')
for current width in px:
width = window.getComputedStyle(document.querySelector('textarea')).getPropertyValue('width')
change 'textarea' to '#history' or like a css selector. or textarea, since a variable is declared to select element.

Graphical glitch using JCrop

I'm having serious troubles implementing Jcrop. I'll show the code regarding Jcrop implementation:
$("#crop-mini").Jcrop({
onChange : updatePositions,
onSelect : updatePositions,
boxWidth : 500,
boxHeight : 400,
keySupport : false,
setSelect : [0, 0, 999999, 999999],
minSize : [10, 10]
});
Where #crop-mini is the <img> tag containing the image. updatePositions is just a function that... updates the selection positions. Pretty straightforward:
function updatePositions(coords)
{
$(".x").val(coords.x);
$(".y").val(coords.y);
$(".w").val(coords.w);
$(".h").val(coords.h);
};
I upload an image, write its url into the <img> tag, fire a fancybox and call JCrop. However, when I resize the selection box, boom, this glitch appears:
It looks like the selected content shows the same image being deformed from positions coords.y (coords is the current selection position) to coords.h+coords.y, and from 0 to coords.w. If I put the selection touching the left corner, you would see the whole image.
By the way, cropping works as expected, and the real coordinates are being passed, so I happen to think that the problem is within the presentation, not the processing. Did I do anything wrong?
This happens when you have max-width set in your css:
img {
max-width: 100%
}
Just add the following rule to fix it:
.image-version img {
max-width: none;
}
Where .image-version is a css class of the element wrapping <img> with id #crop-mini.

html2canvas offscreen

In using html2canvas, I have a stack of DOM objects (relative positioned div's that contain various things), that I wish to create individual thumbnails for. So if there are ten divs, I will create ten thumbnails.
Some of these objects will be offscreen --each of these divs are in a single, encompassing div called "mainDiv". I iterate through the divs within mainDiv and execute the html2canvas on each of them individually.
For those that are onscreen, this works fine. Those that are offscreen do not -- they come back blank. I created a workaround that scrolls the objects to the top of the mainDiv, however this is a kludge and visually unappealing.
Is it possible to specify a DOM object that is not visible? Ideally, I'd like to be able to specify a containing div and have html2canvas ignore the parent visibility, so I can screen capture hidden objects, but barring that, I'd like to be able to screen capture objects that are simply scrolled off screen.
Any thoughts, ideas? Thanks!
---- Here is some example code. Basically, if you had a bunch of divs within a div, iterate through them. I actually do this recursively so that only one gets processed at a time, with the callback calling the recursive function, so it looks something like this:
function recurser(anIndex, callback) {
if (anIndex == -1) {
callback();
return;
}
$(myDivs[anIndex]).html2canvas({
onrendered : function(canvas) {
var img = canvas.toDataURL();
// store the image in an array, do stuff with it, etc.
recurser(--anIndex, callback);
}
})
}
Once the recursive calls are complete, it executes the callback function, which is a function that will do stuff with the images.
Again, all this works fine as long as the objects are visible within the scrolling div that contains all of the divs in #mainDiv. Once any part of the divs are scrolled off, however, they render black. In fact, if half of two divs are scrolled off (the top half of one, the bottom half of the next), they both render completely black.
I know this is an old question but it seemed kind of interesting. Based on my testing it seemed like only position: absolute and position:fixed elements that were outside of the viewport caused this issue. I figured out a solution to the problem.
What I'm doing is making a clone of the element. Setting the styling of the clone to left = 0, top = window.innerHeight (so that it won't be inside the window) and position = 'relative'. Then I append the clone to the body, use html2canvas on the clone, and then remove the clone from the body. This solution works for me in IE, Firefox, and Chrome.
I have created a JSBin example here. Here is the key javascript code:
function hiddenClone(element){
// Create clone of element
var clone = element.cloneNode(true);
// Position element relatively within the
// body but still out of the viewport
var style = clone.style;
style.position = 'relative';
style.top = window.innerHeight + 'px';
style.left = 0;
// Append clone to body and return the clone
document.body.appendChild(clone);
return clone;
}
var offScreen = document.querySelector('.off-screen');
// Clone off-screen element
var clone = hiddenClone(offScreen);
// Use clone with htm2canvas and delete clone
html2canvas(clone, {
onrendered: function(canvas) {
document.body.appendChild(canvas);
document.body.removeChild(clone);
}
});
set 'overflow' as visible for all of its parent elements. After rendered remove it
CSS
.overflowVisible{
overflow:visible !important;
}
JS
$("#container").parents().each(function(ind,elem){
$(elem).addClass("overflowVisible");
});
html2canvas($("#container"), {
onrendered: function(canvas) {
var img =canvas.toDataURL("image/png");
$('body').append(img);
$("#container").parents().each(function(ind,elem){
$(elem).removeClass("overflowVisible");
});
}
});
You can use the latest version of html2canvas. This resolves all images pix-elation issues and rendering object issue. I used the version 0.5.0-beta4 by Erik koopmans refer this and called the html2canvas as below:
$('html,body').scrollTop(0); // Take your <div> / html at top position before calling html2canvas
html2canvas( $("#my_div"), {
useCORS: true,
dpi: 200,
onrendered:function(canvas) {
var str = canvas.toDataURL("image/png");
var contentType = 'image/png';
console.log(str);
b64Data =str;
$('#uploadedImage').val(b64Data); // set image captured by html2canvas
imgFrameSave(); // Call a function to save your image at server side.
}
})
Ah now have you tried Div display: none; and Div display: absolute; (or how you'd like your div to appear - maybe z-index)
arrow1.onclick = (event){
arrow1.style.display = none;
arrow2.style.display = absolute;
}
arrow2.onclick = (event){
arrow2.style.display = none;
arrow1.style.display = absolute;
}
Just use:
{
scrollX: -window.scrollX,
scrollY: -window.scrollY
}
Based on: https://github.com/niklasvh/html2canvas/issues/1878#issuecomment-511678281

change css on scroll event w/ requestAnimation Frame

I want to change the background color of in-viewport elements (using overflow: scroll)
So here was my first attempt:
http://jsfiddle.net/2YeZG/
As you see, there is a brief flicker of the previous color before the new color is painted. Others have had similar problems.
Following the HTML5 rocks instructions, I tried to introduce requestAnimationFrame to fix this problem to no avail:
http://jsfiddle.net/RETbF/
What am I doing wrong here?
Here is a simpler example showing the same problem: http://jsfiddle.net/HJ9ng/
Filed bug with Chromium here: http://code.google.com/p/chromium/issues/detail?id=151880
if it is only the background color, well why don't you just change the parent background color to red and once it scroll just change it to pink?
I change your CSS to that
#dad
{
overflow-y: scroll;
overflow-x: hidden;
width: 100px;
height: 600px;
background-color:red;
}​
I remove some of you Jquery and change it to this
dad.bind('scroll', function() {
dad.css('background-color', 'pink');
});
And I remove this line
iChild.css('backgroundColor', 'red');
But is the Red color it is important that won't work for sure http://jsfiddle.net/2YeZG/5/
I like Manuel's Solution.
But even though I don't get what you're exactly trying to do, I want to point out a few things.
In your fiddle code, I saw that you included Paul Irish's Shim for requestAnimationFrame.
But you never use it.
(It's basically a reliable setTimeOut, nothing else) it's from frame based animations.)
So since you just want to change some CSS properties, I don't see why you would need it. Even if you want transitions, you should rely on CSS transitions.
Other than that your code could look something like
dad.bind('scroll', function() {
dad.css('background-color', 'pink');
eachElemNameHere.css('background-color','randomColor');
});
Also you should ideally not use something like that if you can help it. You should just add and remove class names and add all these properties in your CSS. Makes it work faster.
Also, again I don't quite get it, but you could use the jQuery function to find out each elements' position from the top to have better control.
Your problem seems to be that you only change the background color of the elements which have already been scrolled into view. Your code expects that the browser waits for your code to handle the scroll event before the browser redraws its view. This is most probably not a guarantee given by the HTML spec. That's why it flickers.
What you should do instead is to change the elements which are going to be scrolled into view. This is related to off screen rendering or double buffering as it is called in computer games programming. You build your scene off screen and copy the finished scene to the visible frame buffer.
I modified your first JSFiddle to include a multiplier for the height of the scroll area: http://jsfiddle.net/2YeZG/13/.
dad.bind('scroll', function() {
// new: query multiplier from input field (for demonstration only) and print message
var multiplier = +($("#multiplier")[0].value);
$("#message")[0].innerHTML=(multiplier*100)-100 + "% of screen rendering";
// your original code
var newScrollY = newScrollY = dad.scrollTop();
var isForward = newScrollY > oldScrollY;
var minVal = bSearch(bots, newScrollY, true);
// new: expand covered height by the given multiplier
// multiplier = 1 is similar to your code
// multiplier = 2 would be complete off screen rendering
var newScrollYHt = newScrollY + multiplier * dadHeight;
// your original code (continued)
var maxVal;
for (maxVal = minVal; maxVal < botsLen; maxVal++) {
var nxtTopSide = tops[maxVal];
if (nxtTopSide >= newScrollYHt) {
break;
}
}
maxVal = Math.min(maxVal, botsLen);
$(dadKids.slice(minVal, maxVal)).css('background', 'pink');
});
Your code had a multiplier of 1, meaning that you update the elements which are currently visible (100% of scroll area height). If you set the multiplier to 2, you get complete off screen updates for all your elements. The browser updates enough elements to the new background color so that even a 100% scroll would show updated elements. Since the browser seldom scrolls 100% of the area in one step (depends of the operating system and the scroll method!), it may be sufficient to reduce the multiplier to e.g. 1.5 (meaning 50% off screen rendering). On my machine (Google Chrome, Mac OS X with touch pad) I cannot produce any flicker if the multiplier is 1.7 or above.
BTW: If you do something more complicated than just changing the background color, you should not do it again and again. Instead you should check whether the element has already been updated and perform the change only afterwards.

Categories