Margin Collapsing

来源:互联网 发布:linux snmp服务器配置 编辑:程序博客网 时间:2024/05/18 02:18

Margin Collapsing

Many Web developers run into margin collapsing without realising what it is, or why it is happening. It takes place on virtually every Web page, and in most cases, it creates the desired effect without causing any undesired side effects. However, when it does cause a problem, it usually appears as strange gaps at the top of elements even when they have their top margins set to 0. The typical response is to use an unrelated workaround of some kind, such as positioning everything. Understanding the cause allows more efficient workarounds to be used, especally those that actually deal with the root cause.

Historically, margin collapsing only exists because older browsers like Netscape 3 used to do it before they had CSS, and when CSS was created, it had to describe that behaviour so it could be implemented in CSS.

The box model

The first thing you need to understand is the CSS box model. In its simplest form, a box is a block element, like a DIV, for example. In the middle of the box are the contents. If the relevant styles are set, even if there are no contents, the content size comes from the height and width styles (you will need to ensure that your DOCTYPE triggers standards mode rendering for some browsers, particularly Internet Explorer, for them to get this part correct). Then outside the contents, but still inside the box, is the padding. Then outside the padding, but still inside the box, is the border. Outside the border is the margin. "Box" is only a word, you do not really need to think about it yet. It is more important to understand where the various margin, border, padding, and content exist in relation to each other.

Almost no elements have padding by default. Some of the rare cases where an element has padding by default would be fieldsets, or in some browsers the padding of the body, and the padding of a DL list. For the sake of this tutorial, it is easiest to assume that no block elements have padding.

Most block elements have a default top and bottom margin, such as paragraphs and headings for example, which may have a top and bottom margin of 1em, which computes to 16 pixels if that is the current font size. This is what creates the gap between two paragraphs. It is also what causes margin collapsing to be so important. This tutorial will assume that paragraphs have a 16 pixel top and bottom margin, but you should be aware that different browsers will have very different default margins, and can also change depending on user settings such as font size.

Take the following HTML:

<div>A</div><p>B</p><p>C</p><div>D</div>

Paragraphs may have 16 pixels top and bottom margins, while DIVs have 0. As a result, the gap between the DIV and paragraph is 16 pixels in each case. The gap between the two paragraphs isalso 16 pixels, even though there are two paragraphs, and they may be expected to have 32 pixels between them. This is a margin collapse.

The first and simplest kind of margin collapse

Margin collapsing only happens with top and bottom margins,not left and right, and not at all on inline elements. Margin collapsing happens wherever two (or more) top or bottom margins are touching. The basic idea is that when they touch, instead of getting the sum of the two margins, the bigger one is used, and the other is ignored. So in the case of the two paragraphs, the 16 pixel bottom margin of the first one touches the 16 pixel top margin of the second one. Max(16,16)is 16, so the gap between them is 16 pixels.

Three elements

<p>A</p><p style="margin-top:100px;margin-bottom:50px;"></p><p>B</p>

The middle paragraph has no contents, so it also has no height (since the CSS does not specify a height for it). However, it is still there. The resulting margins are like this:

16 pixelsA16 pixels100 pixels50 pixels16 pixelsB16 pixels

The middle paragraph is 0px high, so its margins are touching. So there are 4 margins touching each other. The gap between A and B is max(16,100,50,16), which is 100 pixels.

Negative margins

Negative margins are special and are dealt with as follows:

  1. Work out what margins are touching.
  2. Ignore all negative margins.
  3. Perform the collapse as if there were no negative margins.
  4. Select the largest of the negative margins, and subtract it from the computed margin from step 3.
<p style="margin-bottom:-5px;">A</p><p style="margin-top:100px;margin-bottom:-50px;"></p><p>B</p>

Step 1:

16A-5100-5016B16

Step 2, ignoring the negative margins:

16A10016B16

Step 3:

The gap between A and B is max(100,16), which is 100 pixels.

Step 4, subtracting the largest negative margin:

Max(5,50) is 50.

100 - 50 = 50

So the resulting gap between A and B is 50 pixels.

Nested margins

This is the part that causes most problems for Web developers.

<div style="margin-top:10px"><div style="margin-top:20px">A</div></div>

There are two margins touching each other: 10 and 20 pixels. The resulting margin will bemax(10,20), which is 20 pixels.

The question is; where does the 20 pixels appear:

  1. Before the outer DIV?
  2. Before the inner DIV but inside the outer DIV?

(In other words, if the background of the outer DIV is red, and the background of the inner DIV is white, should any red be visible?)

The answer is quite simple; the resulting margin always appears as far out as possible - outside the outermost element whose margin is taking part in the collapse. Even though the margin that gets "used" is the one from the inner DIV, it appears outside the outer DIV. So it seems to appear in the wrong place.

This is the part that causes the most unexpected effects. A developer might make the background of a container white, make the background of a DIV inside it blue, then put a paragraph or heading inside it. The expectation is for the blue to extend all the way to the top of the container, but it does not. The paragraph may have a default top margin of 16 pixels, and that collapses to the outside of the DIV because it touches the 0 pixel top margin of the DIV, pushing the DIV down, and leaving a strange gap at the top of the blue.

The solution to the unwanted gap, is to make sure the margin collapse cannot happen. The way to do that is to make sure the 0 pixel top margin of the DIV and the 16 pixel top margin of the paragraph cannot touch each other. This is where the box model becomes useful. It is possible to remove the margin of the paragraph, but it is also possible to manipulate the DIV to insert something between the paragraph's top margin, and the DIV's 0 pixel top margin. The way to do that is to add a top padding or top border of at least 1 pixel of height onto the DIV, since they sit between the two margins.

The following example shows the situation described, with a paragraph inside a DIV that has a blue background, inside a white container (with the solid black border). The paragraph has a dashed border so you can see where it is. The white shows through because the paragraph's margin has collapsed through and is applied to the DIV:

Paragraph

The following example shows the same situation, but with a single pixel of top and bottom padding added to the DIV. The result is two pixels taller because of the padding, but the margin remains on the paragraph, and does not collapse through to the DIV. The margin extends to one pixel away from the black border, with the remaining pixel being the DIV's padding:

Paragraph

The following example uses a similar approach, but uses a (dotted red) border on the DIV instead of padding:

Paragraph

Internet Explorer 7-

Internet Explorer 7- gets most of the margin collapsing behaviour correct. However, it does have some issues.

BODY elements never take part in margin collapsing, since they are considered magical, which means sometimes a strange gap does not show up in Internet Explorer when it does in other browsers, when the collapse happens with the top of the BODY. This is usually easy to solve; just prevent the margin collapse for the other browsers, and it works in Internet Explorer too. (Note that the HTML element's margins never collapse in any browser, this is correct behaviour.)

In rare cases, margin collapsing where an inner element has a bottom border and an outer container has a bottom border, can cause the background of an intermediate element to spill into the container in Internet Explorer.

The more problematic bug is caused by Internet Explorer's strange hasLayout behaviour. This is a fundamental bug in Internet Explorer 7- and affects several other things as well, but this article will only deal with margin collapsing. Setting certain styles on an element makes it "have layout" (a concept unique to Internet Explorer, and not compliant with any standards). The most common style that causes a problem is width. When an element hasLayout it suddenly assumes a minimum height of 1em, and if set to something less in Internet Explorer 6, such as 0.5em, it still uses 1em.

An element has layout when one of the following conditions is true:

  • It has a width and/or a height specified
  • It is an inline-block (display: inline-block)
  • It has absolute positioning (position: absolute)
  • It is a float (float: left, float: right)
  • It is a table element
  • It is transformed (style="zoom: 1")

Height usually does not cause a problem, since setting height will prevent collapsing in other browsers anyway. However, triggering hasLayout on a nested element where the parent has prevented margin collapsing using borders or padding, can cause margins to disappear, or to collapse through the parent irrespective of the padding or borders. Generally, hasLayout is a mess, and it is best to avoid it in places where margins are critical.

The hasLayout issue is dealt with in more detail in another article.

Positioning

When an element has either position:absolute or position:fixed, it is taken out of the flow, and its margins no longer take part in any collapses at all.

<p>A</p><p style="margin-top:100px;margin-bottom:50px;position:absolute;"></p><p>B</p>

The gap between A and B is max(16,16), which is 16 pixels.

Floats

Float also takes elements out of the flow. The margins of its siblings touch as if it was not there, and they collapse while completely ignoring it, as with positioned elements.

When the float is being positioned, it is initially placed as if it had no top margin at all (so its top margin does not take part in any collapsing), and as if it had no subsequent siblings (so any collapsing between its preceding and subsequent siblings is temporarily ignored). Any margins of preceding elements that are due to take part in a margin collapse have their margins collapsed assuming any siblings following the float do not exist. The float is then placed below that collapsed margin. It is then moved downwards by its own top margin. The float is then ignored, and the normal margin collapse is recalculated assuming the float does not exist, but the subsequent siblings do exist, as described in the paragraph above.

The bottom margin of a float does not take part in any collapsing at all. However, elements that are pushed downwards when they are told to clear the margin of a float (using the clear style), may no longer touch a margin that they normally would, so they will not collapse with it.

If the float contains any non-inline elements that have their own margins, these never collapse with the float's margins.

Other special cases

Elements that have non-visible overflow may take part in margin collapsing with the elements around them, but will not take part in any margin collapsing with any elements inside them.

Further special cases and details of margin collapsing are discussed in the CSS 2.1 specification.



转自:http://www.howtocreate.co.uk/tutorials/css/margincollapsing