最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to calculate an element's height considering collapsed margins - Stack Overflow

programmeradmin1浏览0评论

I want to calculate the total "height" of a div element considering the effect of collapsed margins because of child elements, that is, the total space that div element occupies in the document. I'm having a hard time thinking of the best algorithm / approach to do this.

Consider, for example, a div element with a margin-top of 10px and a height of 50px. This div element has a child <h2> element that has a margin-top of 20px. The div's margin will then collapse and the actual "height" of that div will be 70px. However, using jQuery methods, we are only able to get the height of the div without considering it's margins, or considering it's 10 pixel margin which would give us the wrong value:

$(elem).outerHeight() // 50
$(elem).outerHeight(true) // 60

To help illustrate my point, here is a jsfiddle I created with two examples.

My best guess at the moment is we have to iterate over all children of the div in some way and calculate the highest top and bottom margin. According to what I understand from the W3C specification, we can skip this iteration for the top margin if the target div has a top-border-width or a top-padding. Ditto for the bottom margin.

Any help would be greatly appreciated. Thanks!

EDIT:

One (ugly) solution I thought about was wrapping the target div element in another div. Then, we quickly add and remove a transparent borderTop and borderBottom to the wrapping div, measuring it's height in between. The borders will force the wrapping div's margin not to collapse with its children's margins. Something like this:

var collapsedHeight = function( target ) {
   var $wrapper = $('<div />'),
     $target = $(target);

   $wrapper.insertAfter($target);
   $target.appendTo($wrapper);
   $wrapper.css({
       borderTop: '1px solid transparent',
       borderBottom: '1px solid transparent'
   });
   var result = $wrapper.outerHeight() - 2;
   $target.insertAfter($wrapper);
   $wrapper.remove();

   return result;
};

I made a jsFiddle for it here.

I want to calculate the total "height" of a div element considering the effect of collapsed margins because of child elements, that is, the total space that div element occupies in the document. I'm having a hard time thinking of the best algorithm / approach to do this.

Consider, for example, a div element with a margin-top of 10px and a height of 50px. This div element has a child <h2> element that has a margin-top of 20px. The div's margin will then collapse and the actual "height" of that div will be 70px. However, using jQuery methods, we are only able to get the height of the div without considering it's margins, or considering it's 10 pixel margin which would give us the wrong value:

$(elem).outerHeight() // 50
$(elem).outerHeight(true) // 60

To help illustrate my point, here is a jsfiddle I created with two examples.

My best guess at the moment is we have to iterate over all children of the div in some way and calculate the highest top and bottom margin. According to what I understand from the W3C specification, we can skip this iteration for the top margin if the target div has a top-border-width or a top-padding. Ditto for the bottom margin.

Any help would be greatly appreciated. Thanks!

EDIT:

One (ugly) solution I thought about was wrapping the target div element in another div. Then, we quickly add and remove a transparent borderTop and borderBottom to the wrapping div, measuring it's height in between. The borders will force the wrapping div's margin not to collapse with its children's margins. Something like this:

var collapsedHeight = function( target ) {
   var $wrapper = $('<div />'),
     $target = $(target);

   $wrapper.insertAfter($target);
   $target.appendTo($wrapper);
   $wrapper.css({
       borderTop: '1px solid transparent',
       borderBottom: '1px solid transparent'
   });
   var result = $wrapper.outerHeight() - 2;
   $target.insertAfter($wrapper);
   $wrapper.remove();

   return result;
};

I made a jsFiddle for it here.

Share Improve this question edited Oct 27, 2011 at 15:24 vitorbal asked Oct 26, 2011 at 23:05 vitorbalvitorbal 3,05126 silver badges24 bronze badges 2
  • 1 Have you considered avoiding the scenarios where the margin of an ancestor overrides the margin of the element in question? It seems like bad design to me, I wouldn't want that to occur on my pages... – Šime Vidas Commented Oct 27, 2011 at 14:05
  • Hi Šime Vidas, I pletely agree, However I guess my question was more when considering we have no control over the page design. When this code would be in a bookmarklet, for example. – vitorbal Commented Oct 27, 2011 at 15:07
Add a ment  | 

3 Answers 3

Reset to default 5

Consider this hack:

$( elem ).wrap( '<div style="border:1px solid transparent;"></div>' ).parent().height()

The above expression returns 70 which is what you want, right?

So, the idea is to wrap your element in a DIV that has a transparent border set. This border will prevent the margins of your element to interfere with the margins of its previous and next sibling.

Once you get the height value, you can unwrap your element...

For a solution that doesn't involve DOM manipulation, you can achieve the same effect by adding padding to the element being measured and then removing it afterwards.

function getRealHeight(elementP) {
  var
    element = (elementP instanceof jQuery)? elementP : $(element),
    padTop = parseInt(element.css('paddingTop')),
    padBottom = parseInt(element.css('paddingBottom')),
    offset,
    height;

  if (padTop == 0 || padBottom == 0) {
    offset = 0;

    if (padTop == 0) {
      element.css('paddingTop', 1);
      offset += 1;
    }
    if (padBottom == 0) {
      element.css('paddingBottom', 1);
      offset += 1;
    }

    height = (element.outerHeight(true) - offset);

    if (padTop == 0) {
      element.css('paddingTop', '');
    }
    if (padBottom == 0) {
      element.css('paddingBottom', '');
    }
  } else {
    height = element.outerHeight(true);
  }
  return height;
}

The bonus of this solution; you can sidestep the overhead of wrap/unwrap.

You can make it a jQuery plugin:

(function ($) {
    $.fn.extend({
        //plugin name - realHeight
        realHeight: function (options) {

            //Settings list and the default values
            var defaults = {};

            var options = $.extend(defaults, options);

            function getRealHeight(elementP) {
                var
                  element = (elementP instanceof jQuery) ? elementP : $(element),
                  padTop = parseInt(element.css('paddingTop')),
                  padBottom = parseInt(element.css('paddingBottom')),
                  offset,
                  height;

                if (padTop == 0 || padBottom == 0) {
                    offset = 0;

                    if (padTop == 0) {
                        element.css('paddingTop', 1);
                        offset += 1;
                    }
                    if (padBottom == 0) {
                        element.css('paddingBottom', 1);
                        offset += 1;
                    }

                    height = (element.outerHeight(true) - offset);

                    if (padTop == 0) {
                        element.css('paddingTop', '');
                    }
                    if (padBottom == 0) {
                        element.css('paddingBottom', '');
                    }
                } else {
                    height = element.outerHeight(true);
                }
                return height;
            }

            return getRealHeight($(this));
        }
    });
})(jQuery);
发布评论

评论列表(0)

  1. 暂无评论