Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Least expensive way to find the farthest DIVs?

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: All divs are floated to the left.
  • Type 2: All divs are floated to the right.
  • Type 3: All divs have unknown absolute positions, and can be even placed far off the screen.

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:

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

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

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

  4. IE8 and below is not required.

  5. Please use only vanilla Javascript. No frameworks like jQuery etc.

Thanks!

Some visualizations: Type 1 (and 2) divsType 3 divs

like image 286
pilau Avatar asked Nov 12 '22 14:11

pilau


1 Answers

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
}
like image 79
Stuart Avatar answered Nov 15 '22 06:11

Stuart