盒模型与BFC

623 阅读7分钟

1.文档流(normal flow)

我的理解是:文档流是浏览器默认用于排列网页中各种元素的一组规则。也就是说文档流中流动的是元素,它决定了元素如何根据HTML结构自然地上下左右排列,而不需要额外的定位或布局指令。

元素在文档流中主要有两种状态:

  • 在文档流之中
  • 脱离了文档流 (我不遵循你的规则了) 那么元素如何在文档流中显示的呢?

块级元素

  • 块级元素独占一行,从上到下垂直进行排列
  • 默认宽度:填满整个父元素
  • 默认高度:由子元素决定

image.png

行内元素

  • 只占自身大小,从左到右水平排列
  • 默认宽度和高度都由子元素决定

image.png

2.盒模型

CSS 将页面中的所有元素都设置为了一个矩形的盒子,包含内容(content)、内边距(padding)、边框(border)、外边距(margin)。盒子本身的大小与margin无关,它影响的是当前元素与其他元素之间的距离。

image.png

1. 标准盒模型 (content-box)

  • 在这种模型下,width 和 height 属性只应用于内容区域(content area),即不包括 paddingborder 或 margin
  • 如果你为一个元素指定了宽度为200px,并且有10px的内边距和5px的边框,那么这个元素的实际宽度将是230px(200px的内容宽度 + 2*10px的左右内边距 + 2*5px的左右边框)。

2. 怪异盒模型 (border-box)

  • 当使用 box-sizing: border-box; 时,width 和 height 包括了 padding 和 border,但不包括 margin
  • 例如,如果你将一个元素的宽度设为200px,并设置了10px的内边距和5px的边框,那么内容区的宽度将会自动调整以适应总宽度保持在200px之内。

为了改变元素使用的盒模型,你可以使用 box-sizing 属性:

  • box-sizing: content-box; (默认值)使用标准盒模型。
  • box-sizing: border-box; 使用怪异盒模型,其中 width 和 height 决定了元素的外部尺寸,而 padding 和 border 将从内部缩小内容区域。
 * {
    margin: 0;
    padding: 0;
  }
  /* 盒模型 + 块级  */
  .box, .box2,.box3 {
    width: 100px;
    height: 100px;
    padding: 10px;
    border: 1px solid red;
    background-color: green;

  }
  .box2 {
    /* border  以内盒子的大小   IE怪异盒模型*/
    box-sizing: border-box;

  }

  .box3 {
    /*默认值, 标准盒模型  w h content 的大小*/
    box-sizing: content-box;
  }
  </style>
</head>
<body>
  <div class="box"></div>
  <div class="box2"></div>
  <div class="box3"></div>
</body>

image.png

大家根据效果图能很快理解两种模型的不同

3. 行内元素的盒模型

  • 默认情况下行内模型不用于做盒子,不能设置宽高,宽高由内容决定
span {
  /* 行内元素设置了宽高也没用,不会生效 */
  width: 100px;
  height: 100px;
}

display用来设置元素显示的类型

  • inline将元素设置为行内元素
  • block 将元素设置为块元素

3.今天的重头戏-BFC

什么是BFC?

块级格式化上下文(Block Formatting Context)
我的理解:BFC就像是一个“布局保护区”,在这个保护区内有一套自己的布局规则,确保内部元素按照这些规则进行排列,而不受外界干扰。

如何触发BFC?

常见方式:

  • 根元素
  • 浮动元素
  • 绝对定位元素
  • overflow的值不为visible
  • 等等

解决了什么问题?

防止margin塌陷
    <style>
        .outer {
            width: 400px;
            background-color: gray;
            /* 开启一个BFC */
            /* overflow: hidden; */
        }

        .inner1 {
            width: 100px;
            height: 100px;
            background-color: orange;
            /* margin-top: 50px; */
        }

        .inner2 {
            width: 100px;
            height: 100px;
            background-color: yellow;
            /* margin-bottom: 50px; */
        }
    </style>
</head>
<body>
    <div class="outer">
        <div class="inner1">inner1</div>
        <div class="inner2">inner2</div>
    </div>
    <div>别抢我兵线</div>
</body>

注意:outer没有给height,其高度是由inner1和inner2撑起来的 这里我先注释掉给inner1和inner2设置外边距的操作,结果是这样的:

image.png
取消注释后,我们想象的应该是outer不动,inner1上面顶出50px,inner2下面多出50px,但却发生这样的怪状:

screenshots.gif
子元素的外边居然距“穿透”了父元素的边界,就感觉是outer把它子元素的margin值给抢走了

1.发生的原因

当一个父元素包含一个或多个子元素,并且这些子元素有垂直外边距(margin-topmargin-bottom),浏览器可能会将这些外边距与父元素的外边距合并,从而导致塌陷。

2.具体规则
  • 顶部外边距塌陷:

    • 如果父元素没有上内边距(padding-top)、上边框(border-top),并且其第一个子元素有正的 margin-top,那么这个 margin-top 会与父元素的 margin-top 发生塌陷。
  • 底部外边距塌陷:

    • 如果父元素没有下内边距(padding-bottom)、下边框(border-bottom),并且其最后一个子元素有正的 margin-bottom,那么这个 margin-bottom 会与父元素的 margin-bottom 发生塌陷。
3.BFC解决margin塌陷

image.png
给outer开启一个BFC overflow: hidden; overflow: hidden;

screenshots.gif

防止外边距折叠
    * {
        margin: 0;
        padding: 0;
    }
    .box {
        width: 100px;
        height: 100px;
        background-color: lightblue;
        border: 1px solid black;
    }
    .box1 {
        border: 1px solid black;
        margin-bottom: 50px;
    }
    .box2 {
        margin-top: 30px;
    }
</style>
<body>
    <!-- 为什么会发生外边距重合?-->
    <div class="box box1">Box1</div>
    <div class="box box2">Box2</div> -->

</body>

想象一下box1和box2之间应该间隔80px,实际效果却是:

image.png
原因: 块级元素在垂直方向上,一个接一个的放置,并且盒子垂直方向的距离由margin决定,margin-top和margin-bottom分别设置上下边距,同一个BFC的相邻盒子margin会发生重叠,大的说了算

box1和box2都在html这个顶级BFC中,是默认开启的

解决:开启一个新的BFC,每个BFC都有独立渲染区域,不受外界影响

    <!-- 错误做法 -->
    <div class="box box1">Box1</div>
    <div class="box box2" style="overflow: hidden;">Box2</div>

这样是行不通的,box2内部的元素在 BFC 范围内进行布局,box2本身还是与box1相邻,margin属性会相互影响

    <!-- 正确做法 -->
    <div class="box box1">Box1</div>
    <div style="overflow: hidden;">
        <div class="box box2">Box2</div>
    </div>

image.png

注意

添加padding 或 border也可以防止margin塌陷和外边距折叠,本文主要讨论BFC,感兴趣的朋友可以自行查阅

清除浮动
  <style>
  /* 而用 BFC 清除浮动的原理就是:计算 BFC 的高度时,浮动元素也参与计算。只要触发父元素的 BFC 即可。 */
  .parent {
    background-color: red;
    overflow: hidden;
  }
  .child {
    float: left;
    height: 200px;
    width: 200px;
    background-color: green;
  }
  </style>
</head>
<body>
  <div class="parent">
    <div class="child"></div>
  </div>
</body>

screenshots.gif

由于parent包含了一个浮动元素,导致无法正确计算高度

发生原因:当一个父元素只包含浮动的子元素时,这些浮动元素离开了文档流,不会占据空间,相当于飘走了,导致父元素的高度塌陷为零。
解决:给父元素开启BFC,计算BFC的高度时,浮动元素也参与计算

  .container {
    border: 2px solid black;
    padding: 10px;
}

.float-box {
    float: left;
    width: 150px;
    height: 100px;
    background-color: lightblue;
    margin: 10px;
}

.bfc-box {
    width: 300px;
    height: auto;
    background-color: lightgreen;
    /* overflow: auto; */
    padding: 10px;
    border: 1px solid #ccc;
}
  </style>
</head>
<body>
  <div class="container">
    <div class="float-box">这是一个左浮动的盒子</div>
    <div class="bfc-box">
        <p>这是创建了 BFC 的盒子内部的内容。</p>
        <p>注意,这个内容不会被外部的浮动元素影响。</p>
    </div>
  </div>
</body>

浮动元素不会占据空间,后面的bfc-box在不开启BFC的情况下,会产生一种被浮动元素覆盖,本身的内容又环绕着float-box的效果

image.png

bfc-box开启BFC后

image.png

原因:BFC 区域不会与float box重叠 ,独立渲染区域,不受外界影响

4.总结

  • BFC和文档流?
    • 文档流 是所有元素默认遵循的布局模式,而 BFC 提供了一种机制,允许开发者通过创建独立的布局环境来更精细地控制某些元素的布局,解决特定的布局挑战。
  • 开启BFC
    • overflow: hidden | auto | scroll 非visible
    • display: flex | inline-block | grid
    • position: absolute
    • float: left | right
    • 等等
  • BFC的主要规则:
    • html是最顶级的BFC
    • block level box 垂直方向,一个接一个的放置,且宽度100%
    • 盒子垂直方向的距离由margin决定,margin-top和margin-bottom分别设置上下边距 同一个BFC的相邻盒子margin会发生重叠,大的说了算
    • BFC 区域不会与float box重叠
    • 独立渲染区域,不受外界影响
    • 计算BFC的高度时,浮动元素也参与计算

image.png

作者只是一个刚接触前端的小白,有说的不对的地方,恳请大佬指出,有不同观点的也欢迎交流讨论💕💕