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)
I have a routine that sizes elements in a page to fit snugly within their parent. In most cases, it is working admirably, but in Firefox (JUST Firefox - Chrome, IE, etc are fine) it is fumbling on the first attempt in one particular instance - a div nested within a fieldset fails to resize on the first attempt, but succeeds on the second (and subsequent) attempts.
Each element is sized relative to its parent using the following:
function resizeChild(elem) {
// Get gutter based on margins, borders, padding, etc
var gutter = getGutter(elem); // returns obj with x and y properties
var parent = elem.parentElement;
var parentStyles = window.computedStyle(parent);
var targetWidth = (parseInt(parentStyles['width']) - gutter.x;
var widthPx = targetWidth + 'px';
// prototype.js setStyle shortcut
elem.setStyle({
width: widthPx,
maxWidth: widthPx,
minWidth: widthPx
});
}
I run this in a loop, iterating over every element with a particular CSS class.
According to the Firefox debugger, the outer element (the fieldset) is always being resized before the inner div. I can inspect the element, and see the style attributes being set appropriately. However, on the next iteration of the loop, when the parent is being evaluated (I can see in the javascript property inspector that the parent is indeed the fieldset), the value for width that is returned for the computed style is the previous, unmodified value, thus the inner div is resized incorrectly.
Can somebody shed some light on this please?
Edits after comments:
parent.clientWidth returns 0.
Not sure if this is relevant, but a parent div of the fieldset had display set to none shortly prior the resize operation being called. However, at the point at which the fieldset was resized, the display of the div was set to inline-block. I don't think this would make a difference, but then I'm not well educated on some of the particular behaviours of Firefox in this scenario.
I found a solution to this, although it's a little situational.
It seems that if the width of the parent element has been dynamically modified using prototype.js#Element.setStyle() (and, for all I know, other libraries that directly modify the style attribute), then the computedStyle() method won't reflect the change until all changes have completed.
The solution was to check to see if the parent element of the element being resized also had the CSS class that flagged the elements for resize, and if it did, get the size from the style attribute instead of using computedStyle(). Here's the full function, with modifications:
function resizeFullwidth() {
$$('*.fullWidth').each(function(elem, i) {
// Get gutter based on margins, borders, padding, etc
var gutter = getGutter(elem); // returns obj with x and y properties
var parent = elem.parentElement;
var parentStyles = (
parent.hasClassName('fullWidth')
? window.computedStyle(parent)
: parent.style);
var targetWidth = (parseInt(parentStyles['width']) - gutter.x;
var widthPx = targetWidth + 'px';
// prototype.js setStyle shortcut
elem.setStyle({
width: widthPx,
maxWidth: widthPx,
minWidth: widthPx
});
});
}
This now works correctly in all browsers :)
Thanks very much for your help, people :)
Have you tried var targetWidth = parent.clientWidth ?
See : MDN Element.clientWidth
If I have div A and div B, is there a way to say A.width = b.width = MAX(a.width, b.width) ? That is, whichever has the largest inner content would dictate how large both are.
The actual problem I'm trying to solve is with columns - left, middle, and right. I want the left and right to be the same fixed width (but this could vary depending on their content).
It is not possible to use CSS to achieve this. However, if there is a way to do it with a JS-based solution. Here I am using jQuery. Let's say you have two divs, with classes a and b respectively.
$(function() {
function equalizeSize($ele) {
if($ele.length > 1) {
// Let CSS automatically calculate natural width first
$ele.css({ width: 'auto' });
// And then we fetch the newly calculated widths
var maxWidth = Math.max.apply(Math, $ele.map(function(){ return $(this).outerWidth(); }).get());
$ele.css({ width: maxWidth });
}
}
// Run when DOM is ready
equalizeSize($('.a, .b'));
// Run again when viewport has been resized, which **may** affect your div width.
// This is optional, but good to have
// ps: You might want to look into throttling the resize function
$(window).resize(equalizeSize($('.a, .b')));
});
See proof-of-concept fiddle here: http://jsfiddle.net/teddyrised/N4MMg/
The advantages of this simple function:
Allows you to dictate what elements you want to equalize widths with.
Uses the .map() function to construct an array, which we then use Math.max.apply to get the maximum value in the array
Forces automatic calculation of width when the function first fires (especially when resizing the viewport)
Allows you to call to recalculate the size again, using the handler equalizeSize() when you change the content in the divs... you can call the function again, say, after an AJAX call that appends content to either element.
It is not very clear what you want from the description. but I can rewrite your code this way.
var properWidth = Math.max($("#a").width(), $("#b").width());
$("#a").css("width", properWidth + "px");
$("#b").css("width", properWidth + "px");
I am not sure if it is this kind of solution you want.
I'm not sure there is a way to do it like that. But why not make a default function to set the size:
function changeSize(w, h)
{
A.setAttribute('style', 'width:'+w+'; height:'+h);
b.setAttribute('style', 'width:'+w+'; height:'+h);
}
Working fiddle: http://jsfiddle.net/kychan/ER2zZ/
I am writing a validator for "visual correctness" of html files. The goal is to detect too wide elements.
Here is a demo of my problem.
The dotted red line is an indicator of the max width of the document (200px in this example). The first paragraph is fine, but the second is too wide. Nevertheless, all of the following commands still return "200px" as the width:
// all return 200, but the value should be larger
$('#two').width();
$('#two').outerWidth();
$('#two').prop('clientWidth');
Please see the Fiddle for more details.
How can i detect such oversized elements?
Updated question: Better to ask, how can i detect text that exceeds the borders of their parent elements?
Updated requirement: I am not allowed to change anything in the source HTML or CSS. But i can do anything i want with jQuery to modify the document, so that i can detect those too wide elements.
As others have said, temporarily wrap the text node in an inline element.
var two = document.getElementById('two'),
text = two.firstChild,
wrapper = document.createElement('span');
// wrap it up
wrapper.appendChild(text);
two.appendChild(wrapper);
// better than bad, it's good.
console.log(wrapper.offsetWidth);
// put it back the way it was.
two.removeChild(wrapper);
two.appendChild(text);
http://jsfiddle.net/vv68y/12/
Here is a getInnerWidth function that should be useful to you. Pass it an element and it will handle the wrapping and unwrapping.
function getInnerWidth(element) {
var wrapper = document.createElement('span'),
result;
while (element.firstChild) {
wrapper.appendChild(element.firstChild);
}
element.appendChild(wrapper);
result = wrapper.offsetWidth;
element.removeChild(wrapper);
while (wrapper.firstChild) {
element.appendChild(wrapper.firstChild);
}
return result;
}
http://jsfiddle.net/vv68y/13/
scrollWidth will do it:
$("#two").get()[0].scrollWidth
or
getElementById("two").scrollWidth
this outputs 212px, the real width you are looking for.
This effect is called "shrinkwrapping", and there's a couple of ways to determine the "real" width of the element.
Float
One of the ways that you can use is to float your <p> element which will force it as small as possible, but you'll need to use a clearfix if anything inside your div is floating:
#two { float: left; }
Inline-block element
Inserting an inline element should work.
<p>content</p>
would become
<p><span>content</span></p>
Absolutely positioned element
Changing the element position to be absolute should also work:
#two { position: absolute; }
If you can't statically change the markup or the style, you can always change them dynamically through JavaScript.
(absolutely positioned element)
var realWidth = $("#two").css("position", "absolute").width();
(float)
var realWidth = $("#two").css("float", "left").width();
(inline-block element)
var t = $("#two").html();
var realWidth = $("#two")
.empty()
.append($("<span>").html(t))
.width();
Apply word-wrap: break-word; to it.. so the word will break and there won't be any text going out of the container... btw you can't check the width of the text which is going out of the container.
Example
Update: You can check if the width of text in it is bigger than the width of the container like this
As others have pointed out, changing the position of the element to absolute also works.
Doing this will result in an inline-style which can mess with your css afterwards if you don't watch out. Here is a solution to get rid of the inline style again.
//Change position to absolute
$('#two').css("position", "absolute");
var textWidth = $('#two').width();
//Get rid of the inline style again
$('#two').removeStyle("position");
//Plugin format
(function ($) {
$.fn.removeStyle = function (style) {
var search = new RegExp(style + '[^;]+;?', 'g');
return this.each(function () {
$(this).attr('style', function (i, style) {
return style.replace(search, '');
});
});
};
}(jQuery));
The element itself is constrained to 200px, but the text inside spills out. If you insert a span (or any other inline element) inside the P tag it works fine.
http://jsfiddle.net/will/vv68y/5/
Hope that helps :)
I need to get height of an element that is within a div that is hidden. Right now I show the div, get the height, and hide the parent div. This seems a bit silly. Is there a better way?
I'm using jQuery 1.4.2:
$select.show();
optionHeight = $firstOption.height(); //we can only get height if its visible
$select.hide();
You could do something like this, a bit hacky though, forget position if it's already absolute:
var previousCss = $("#myDiv").attr("style");
$("#myDiv").css({
position: 'absolute', // Optional if #myDiv is already absolute
visibility: 'hidden',
display: 'block'
});
optionHeight = $("#myDiv").height();
$("#myDiv").attr("style", previousCss ? previousCss : "");
I ran into the same problem with getting hidden element width, so I wrote this plugin call jQuery Actual to fix it. Instead of using
$('#some-element').height();
use
$('#some-element').actual('height');
will give you the right value for hidden element or element has a hidden parent.
Full documentation please see here. There is also a demo include in the page.
Hope this help :)
You are confuising two CSS styles, the display style and the visibility style.
If the element is hidden by setting the visibility css style, then you should be able to get the height regardless of whether or not the element is visible or not as the element still takes space on the page.
If the element is hidden by changing the display css style to "none", then the element doesn't take space on the page, and you will have to give it a display style which will cause the element to render in some space, at which point, you can get the height.
I've actually resorted to a bit of trickery to deal with this at times. I developed a jQuery scrollbar widget where I encountered the problem that I don't know ahead of time if the scrollable content is a part of a hidden piece of markup or not. Here's what I did:
// try to grab the height of the elem
if (this.element.height() > 0) {
var scroller_height = this.element.height();
var scroller_width = this.element.width();
// if height is zero, then we're dealing with a hidden element
} else {
var copied_elem = this.element.clone()
.attr("id", false)
.css({visibility:"hidden", display:"block",
position:"absolute"});
$("body").append(copied_elem);
var scroller_height = copied_elem.height();
var scroller_width = copied_elem.width();
copied_elem.remove();
}
This works for the most part, but there's an obvious problem that can potentially come up. If the content you are cloning is styled with CSS that includes references to parent markup in their rules, the cloned content will not contain the appropriate styling, and will likely have slightly different measurements. To get around this, you can make sure that the markup you are cloning has CSS rules applied to it that do not include references to parent markup.
Also, this didn't come up for me with my scroller widget, but to get the appropriate height of the cloned element, you'll need to set the width to the same width of the parent element. In my case, a CSS width was always applied to the actual element, so I didn't have to worry about this, however, if the element doesn't have a width applied to it, you may need to do some kind of recursive traversal of the element's DOM ancestry to find the appropriate parent element's width.
Building further on user Nick's answer and user hitautodestruct's plugin on JSBin, I've created a similar jQuery plugin which retrieves both width and height and returns an object containing these values.
It can be found here:
http://jsbin.com/ikogez/3/
Update
I've completely redesigned this tiny little plugin as it turned out that the previous version (mentioned above) wasn't really usable in real life environments where a lot of DOM manipulation was happening.
This is working perfectly:
/**
* getSize plugin
* This plugin can be used to get the width and height from hidden elements in the DOM.
* It can be used on a jQuery element and will retun an object containing the width
* and height of that element.
*
* Discussed at StackOverflow:
* http://stackoverflow.com/a/8839261/1146033
*
* #author Robin van Baalen <robin#neverwoods.com>
* #version 1.1
*
* CHANGELOG
* 1.0 - Initial release
* 1.1 - Completely revamped internal logic to be compatible with javascript-intense environments
*
* #return {object} The returned object is a native javascript object
* (not jQuery, and therefore not chainable!!) that
* contains the width and height of the given element.
*/
$.fn.getSize = function() {
var $wrap = $("<div />").appendTo($("body"));
$wrap.css({
"position": "absolute !important",
"visibility": "hidden !important",
"display": "block !important"
});
$clone = $(this).clone().appendTo($wrap);
sizes = {
"width": $clone.width(),
"height": $clone.height()
};
$wrap.remove();
return sizes;
};
Building further on Nick's answer:
$("#myDiv").css({'position':'absolute','visibility':'hidden', 'display':'block'});
optionHeight = $("#myDiv").height();
$("#myDiv").css({'position':'static','visibility':'visible', 'display':'none'});
I found it's better to do this:
$("#myDiv").css({'position':'absolute','visibility':'hidden', 'display':'block'});
optionHeight = $("#myDiv").height();
$("#myDiv").removeAttr('style');
Setting CSS attributes will insert them inline, which will overwrite any other attributes you have in your CSS file. By removing the style attribute on the HTML element, everything is back to normal and still hidden, since it was hidden in the first place.
You could also position the hidden div off the screen with a negative margin rather than using display:none, much like a the text indent image replacement technique.
eg.
position:absolute;
left: -2000px;
top: 0;
This way the height() is still available.
I try to find working function for hidden element but I realize that CSS is much complex than everyone think. There are a lot of new layout techniques in CSS3 that might not work for all previous answers like flexible box, grid, column or even element inside complex parent element.
flexibox example
I think the only sustainable & simple solution is real-time rendering. At that time, browser should give you that correct element size.
Sadly, JavaScript does not provide any direct event to notify when element is showed or hidden. However, I create some function based on DOM Attribute Modified API that will execute callback function when visibility of element is changed.
$('[selector]').onVisibleChanged(function(e, isVisible)
{
var realWidth = $('[selector]').width();
var realHeight = $('[selector]').height();
// render or adjust something
});
For more information, Please visit at my project GitHub.
https://github.com/Soul-Master/visible.event.js
demo: http://jsbin.com/ETiGIre/7
Following Nick Craver's solution, setting the element's visibility allows it to get accurate dimensions. I've used this solution very very often. However, having to reset the styles manually, I've come to find this cumbersome, given that modifying the element's initial positioning/display in my css through development, I often forget to update the related javascript code. The following code doesn't reset the styles per say, but removes the inline styles added by javascript:
$("#myDiv")
.css({
position: 'absolute',
visibility: 'hidden',
display: 'block'
});
optionHeight = $("#myDiv").height();
optionWidth = $("#myDiv").width();
$("#myDiv").attr('style', '');
The only assumption here is that there can't be other inline styles or else they will be removed aswell. The benefit here, however, is that the element's styles are returned to what they were in the css stylesheet. As a consequence, you can write this up as a function where an element is passed through, and a height or width is returned.
Another issue I've found of setting the styles inline via js is that when dealing with transitions through css3, you become forced to adapt your style rules' weights to be stronger than an inline style, which can be frustrating sometimes.
By definition, an element only has height if it's visible.
Just curious: why do you need the height of a hidden element?
One alternative is to effectively hide an element by putting it behind (using z-index) an overlay of some kind).
In my circumstance I also had a hidden element stopping me from getting the height value, but it wasn't the element itself but rather one of it's parents... so I just put in a check for one of my plugins to see if it's hidden, else find the closest hidden element. Here's an example:
var $content = $('.content'),
contentHeight = $content.height(),
contentWidth = $content.width(),
$closestHidden,
styleAttrValue,
limit = 20; //failsafe
if (!contentHeight) {
$closestHidden = $content;
//if the main element itself isn't hidden then roll through the parents
if ($closestHidden.css('display') !== 'none') {
while ($closestHidden.css('display') !== 'none' && $closestHidden.size() && limit) {
$closestHidden = $closestHidden.parent().closest(':hidden');
limit--;
}
}
styleAttrValue = $closestHidden.attr('style');
$closestHidden.css({
position: 'absolute',
visibility: 'hidden',
display: 'block'
});
contentHeight = $content.height();
contentWidth = $content.width();
if (styleAttrValue) {
$closestHidden.attr('style',styleAttrValue);
} else {
$closestHidden.removeAttr('style');
}
}
In fact, this is an amalgamation of Nick, Gregory and Eyelidlessness's responses to give you the use of Gregory's improved method, but utilises both methods in case there is supposed to be something in the style attribute that you want to put back, and looks for a parent element.
My only gripe with my solution is that the loop through the parents isn't entirely efficient.
One workaround is to create a parent div outside the element you want to get the height of, apply a height of '0' and hide any overflow. Next, take the height of the child element and remove the overflow property of the parent.
var height = $("#child").height();
// Do something here
$("#parent").append(height).removeClass("overflow-y-hidden");
.overflow-y-hidden {
height: 0px;
overflow-y: hidden;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="parent" class="overflow-y-hidden">
<div id="child">
This is some content I would like to get the height of!
</div>
</div>
Here's a script I wrote to handle all of jQuery's dimension methods for hidden elements, even descendants of hidden parents. Note that, of course, there's a performance hit using this.
// Correctly calculate dimensions of hidden elements
(function($) {
var originals = {},
keys = [
'width',
'height',
'innerWidth',
'innerHeight',
'outerWidth',
'outerHeight',
'offset',
'scrollTop',
'scrollLeft'
],
isVisible = function(el) {
el = $(el);
el.data('hidden', []);
var visible = true,
parents = el.parents(),
hiddenData = el.data('hidden');
if(!el.is(':visible')) {
visible = false;
hiddenData[hiddenData.length] = el;
}
parents.each(function(i, parent) {
parent = $(parent);
if(!parent.is(':visible')) {
visible = false;
hiddenData[hiddenData.length] = parent;
}
});
return visible;
};
$.each(keys, function(i, dimension) {
originals[dimension] = $.fn[dimension];
$.fn[dimension] = function(size) {
var el = $(this[0]);
if(
(
size !== undefined &&
!(
(dimension == 'outerHeight' ||
dimension == 'outerWidth') &&
(size === true || size === false)
)
) ||
isVisible(el)
) {
return originals[dimension].call(this, size);
}
var hiddenData = el.data('hidden'),
topHidden = hiddenData[hiddenData.length - 1],
topHiddenClone = topHidden.clone(true),
topHiddenDescendants = topHidden.find('*').andSelf(),
topHiddenCloneDescendants = topHiddenClone.find('*').andSelf(),
elIndex = topHiddenDescendants.index(el[0]),
clone = topHiddenCloneDescendants[elIndex],
ret;
$.each(hiddenData, function(i, hidden) {
var index = topHiddenDescendants.index(hidden);
$(topHiddenCloneDescendants[index]).show();
});
topHidden.before(topHiddenClone);
if(dimension == 'outerHeight' || dimension == 'outerWidth') {
ret = $(clone)[dimension](size ? true : false);
} else {
ret = $(clone)[dimension]();
}
topHiddenClone.remove();
return ret;
};
});
})(jQuery);
If you've already displayed the element on the page previously, you can simply take the height directly from the DOM element (reachable in jQuery with .get(0)), since it is set even when the element is hidden:
$('.hidden-element').get(0).height;
same for the width:
$('.hidden-element').get(0).width;
(thanks to Skeets O'Reilly for correction)