Let's say we have a container div of unknown width
and height
which is smaller than the full dimensions of the body.
This container div contains an unknown number of divs. All divs in the container are of varying sizes and apart from that have identical CSS as one of these 3 types:
Type 1 and 2 divs have margins and may have padding as well. Type 3 divs may have either, neither or both.
Given that the type of divs in the container is unknown - the ultimate goal is to add a secondary container, inside the original, large enough to accommodate all divs in the viewport (with overflow: scroll). What is the least expensive way to calculate that?
Some things to keep in mind:
divs may or may not (as a whole) have box-sizing: border-box
. This will affect how their borders, margins and padding will be rendered and should be taken into account as well.
In cases of types 1 and 2 (floating divs), the divs are meant to stack horizontally - i.e., the container should grow to fit their total width. This should be taken into account.
A right margin on the rightmost div, for example, should be taken into calculation and should increase the container's width. This same goes for top-, bottom-, and leftmost divs.
IE8 and below is not required.
Please use only vanilla Javascript. No frameworks like jQuery etc.
Thanks!
Some visualizations:
You may have to do the performance testing yourself to see which method is fastest, but anyway here is one quite quick way. (jsfiddle)
function getOuterBoxDimensions() {
var original = document.getElementById('original'),
divs = original.getElementsByTagName('div'),
left = 0,
right = original.offsetWidth,
top = 0,
bottom = original.offsetHeight;
for (var i = 0, div; div = divs[i++];) {
if (div.offsetTop < top) {
top = div.offsetTop;
}
if (div.offsetTop + div.offsetHeight > bottom) {
bottom = div.offsetTop + div.offsetHeight;
}
if (div.offsetLeft < left) {
left = div.offsetLeft;
}
if (div.offsetLeft + div.offsetWidth > right) {
right = div.offsetLeft + div.offsetWidth;
}
}
return {
top: top,
left: left,
bottom: bottom,
right: right
};
// Note that dimensions are relative to the original div top left
}
As far as I know offsetLeft, offsetWidth etc. will return the right dimensions regardless of whether box-sizing: border-box
is set or not. If you have divs within the interior divs then things will get a bit more complicated - you'll want to check only the divs that are childNodes of the original.
EDIT: Here is an extended version that properly takes into account margins and expands the new container to accommodate all floating divs in a single row (see discussion in comments). http://jsfiddle.net/m7N2J/10/
function getOuterBoxDimensions() {
var original = document.getElementById('original'),
divs = original.getElementsByTagName('div'),
left = 0,
right = original.offsetWidth,
top = 0,
bottom = original.offsetHeight,
d = document.defaultView,
style, marginTop, marginBottom, marginLeft, marginRight, float, floatWidth = 0;
for (var i = 0, div; div = divs[i++];) {
if (style = div.currentStyle) {
// May be possible to exclude this if IE7/8 not needed
marginTop = parseFloat(style.marginTop);
marginBottom = parseFloat(style.marginBottom);
marginLeft = parseFloat(style.marginLeft);
marginRight = parseFloat(style.marginRight);
float = style.float;
}
else {
style = d.getComputedStyle(div, null);
marginTop = parseFloat(style.getPropertyValue('margin-top'));
marginBottom = parseFloat(style.getPropertyValue('margin-bottom'));
marginLeft = parseFloat(style.getPropertyValue('margin-left'));
marginRight = parseFloat(style.getPropertyValue('margin-right'));
float = style.getPropertyValue('float');
}
if (float == 'left' || float == 'right') {
floatWidth += div.offsetWidth + marginLeft + marginRight;
if (div.offsetHeight + marginBottom > bottom) {
bottom = div.offsetHeight + marginBottom;
}
}
else {
if (div.offsetTop - marginTop < top) {
top = div.offsetTop - marginTop;
}
if (div.offsetTop + div.offsetHeight + marginBottom > bottom) {
bottom = div.offsetTop + div.offsetHeight + marginBottom;
}
if (div.offsetLeft < left - marginLeft) {
left = div.offsetLeft - marginLeft;
}
if (div.offsetLeft + div.offsetWidth + marginRight > right) {
right = div.offsetLeft + div.offsetWidth + marginRight;
}
}
}
if (right < left + floatWidth) {
right = left + floatWidth;
}
return {
top: top,
left: left,
bottom: bottom,
right: right
};
// Note that dimensions are relative to the original div
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With