Block Formatting Contexts, Collapsing Margins and

2019-01-27 06:01发布

问题:

In order to understand what does a block formatting context do, I'm trying to find out what's going on when a BFC is not created.

I took the following demo from Everything you Know about Clearfix is Wrong:

.wrapper {
  width: 740px;
  background: #cccccc;
}
.leftSidebar {
  float: left;
  width: 200px;
}
.rightSidebar {
  float: right;
  width: 200px;
}
.mainContent {
  padding-right: 200px;
  padding-left: 200px;
}
.floatMe {
  float: left;
  background: teal;
  color: #fff;
}
<div class="wrapper">
  <div class="leftSidebar">
    <h2>Heading</h2>
    <pre>.leftSidebar {
  float:left;
  width:200px;
}</pre>
  </div>
  <div class="rightSidebar">
    <h2>Heading</h2>
    <pre>.rightSidebar {
  float:right;
  width:200px;
}</pre>
  </div>
  <div class="mainContent">
    <h2>Heading</h2>
    <pre>.mainContent {
  padding-right:200px;
  padding-left:200px;
}</pre>
    <div class="floatMe">
      <pre>.floatMe {
  float:left;
  background:teal;
  color:#fff;
}</pre>
    </div>
  </div>
</div>

According to that article(emphasis mine):

In modern browsers:

All elements belong to the same block formatting context so adjacent margins collapse. The heading’s margin “sticks out” of the wrapper to butt against the p. Unlike in IE, it is that margin (not the one on the black box) that creates the gap above the wrapper.

I cannot understand what does "the same block formatting context" refers to. I want to know why such a weird layout is produced without a block formatting context.

I've tried to figure out the exact layout by adding * {border: 1px solid blue;} to CSS, but the overall layout changed greatly after this change: now it behaves as if wrapper is a block formatting context!

.wrapper {
  width: 740px;
  background: #cccccc;
}
.leftSidebar {
  float: left;
  width: 200px;
}
.rightSidebar {
  float: right;
  width: 200px;
}
.mainContent {
  padding-right: 200px;
  padding-left: 200px;
}
.floatMe {
  float: left;
  background: teal;
  color: #fff;
}
* {
  border: 1px solid blue;
}
<div class="wrapper">
  <div class="leftSidebar">
    <h2>Heading</h2>
    <pre>.leftSidebar {
  float:left;
  width:200px;
}</pre>
  </div>
  <div class="rightSidebar">
    <h2>Heading</h2>
    <pre>.rightSidebar {
  float:right;
  width:200px;
}</pre>
  </div>
  <div class="mainContent">
    <h2>Heading</h2>
    <pre>.mainContent {
  padding-right:200px;
  padding-left:200px;
}</pre>
    <div class="floatMe">
      <pre>.floatMe {
  float:left;
  background:teal;
  color:#fff;
}</pre>
    </div>
  </div>
</div>

Please tell me what's going on.

回答1:

Good question, got me thinking a lot!

There are lot of concepts at play here, so I'll get to them one by one:

Buggy IE:

Whatever is mentioned in this old article about IE can be easily ignored if you do not have to design for IE7 or IE8 compatibility mode. This behavior is due to hasLayout property used internally by IE7.

See this MSDN doc for IE7:

What is "HasLayout" and why is it important?

There are several bugs in Internet Explorer that can be worked around by forcing "a layout" (an IE internal data structure) on an element.

Clearly this is a non-standard workaround and along with brings up a lot of inconsistencies. Read about this here too.


Block Formatting Context (BFC):

Excerpts from this MDN doc:

A block formatting context is a part of a visual CSS rendering of a Web page. It is the region in which the layout of block boxes occurs and in which floats interact with each other.

BFCs are very important for positioning and clearing of floated elements- floated elements affects only within the same BFCs. When you float an element, it is taken out of the flow and reinserted by "floating".

See the examples below:

  1. The inside of wrapper is a BFC where you float one div to left and another to the right.

  2. The floated elements are reinserted into the BFC while rendering around the element that is not floated.

  3. As you have not cleared the floating in the BFC, the wrapper height will extend to the size of the element that is not floated.

        body{
          margin: 0;
        }
        *{
          box-sizing: border-box;
        }
        .wrapper{
          border: 1px solid;
        }
        .wrapper > * {
          display: inline-block;
          border: 1px solid red;
          width: 33.33%;
          height: 100px;
        }
        .left{
          float: left;
        }
        .right{
          float: right;
        }
        .center{
          height: 50px;
        }
        <div class="wrapper">
          <div class="left">Left</div>
          <div class="center">Center</div>
          <div class="right">Right</div>
        </div>

  4. See what happens when you clear the floating in the BFC- now the heights will behave normally in the wrapper BFC.

        body{
          margin: 0;
        }
        *{
          box-sizing: border-box;
        }
        .wrapper{
          border: 1px solid;
        }
        .wrapper > * {
          display: inline-block;
          border: 1px solid red;
          width: 33.33%;
          height: 100px;
        }
        .left{
          float: left;
        }
        .right{
          float: right;
        }
        .center{
          height: 50px;
        }
        .wrapper:after{
          content: '';
          display: block;
          clear: both;
        }
        <div class="wrapper">
          <div class="left">Left</div>
          <div class="center">Center</div>
          <div class="right">Right</div>
        </div>


Collapsing Margins:

Top and bottom margins of blocks are sometimes combined (collapsed) into a single margin whose size is the largest of the margins combined into it, a behavior known as margin collapsing.

Margins collapse for adjacent blocks, parent and first/last child and empty blocks. See more about margin collapsing in this MDN doc.

Also note that:

Margins of floating and absolutely positioned elements never collapse.



So what really happens here?

  1. So now you will have understood about BFCs and also how floating containers work in first case (when you have no borders specified) - that's why floatMe stays out of its immediate mainContent wrapper and exactly why the height of wrapper and mainContent is as it looks there.

  2. Layout and IE referred to are only in IE7 and is non-standard.

  3. Everything else that happens is because of margin collapsing:

    a. h2 and pre margins collapse (adjacent siblings)

    b. mainContent shifts a little bit to the top to collapse with the margin on the body (Parent and first/last child)

    c. As wrapper takes the height of mainContent, the wrapper height is also shifted upwards.

    d. What happens when you apply borders is that the margin collapsing in (b) above is nullified! (see MDN doc above as to why)



Hope things are looking better now. Cheers!