Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

了解BFC特性,轻松实现自适应布局 #42

Open
maicFir opened this issue Aug 11, 2022 · 0 comments
Open

了解BFC特性,轻松实现自适应布局 #42

maicFir opened this issue Aug 11, 2022 · 0 comments

Comments

@maicFir
Copy link
Owner

maicFir commented Aug 11, 2022

BFC(Block Formatting Context)俗称块级格式上下文,初次看到这词似乎有点不是很理解,通俗解释就是一个独立区域决定了内部元素的排放,以及内部元素与外部元素的相互作用关系

正文开始...

BFC 是什么

俗称块级格式上下文,一块独立的区域决定了内部元素的位置排列,以及内部元素与外部元素的作用关系

BFC 特点

我们先了解下BFC有什么特点

1、垂直方向,相邻BFC的块级元素会产生外边距合并

2、BFC 包含浮动元素,浮动会触发新的 BFC 产生

3、已经确定的 BFC 区域不会与相邻 BFC 的浮动元素边距发生重合

针对以上几点我来具体深究一下 BFC 的特性到底有何区别,在什么样的场景下会比较触发BFC

新建一个index.html测试

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>BFC</title>
    <style>
      * {
        padding: 0;
        margin: 0;
      }
      .wrap-box {
        width: 300px;
      }
      .inner-box {
        width: 100px;
        height: 50px;
      }
      .inner-box:nth-of-type(2n + 1) {
        background-color: red;
      }
    </style>
  </head>
  <body>
    <div class="wrap-box">
      <div class="inner-box">1</div>
      <div class="inner-box">2</div>
      <div class="inner-box">3</div>
    </div>
  </body>
</html>

不出意外在wrap-box这个 BFC 中,内部元素垂直单行排列

这说明块级格式上下文,在wrap-box这个元素决定了内部的元素排放,因为子元素始终是被包裹起来的,因为是块级元素,所以单行排列。

接下来我们将子元素添加外边距margin:10px 0;

* {
  padding: 0;
  margin: 0;
}
.wrap-box {
  width: 300px;
}
.inner-box {
  width: 100px;
  height: 50px;
  margin: 10px 0;
}


另外我们看下wrap-box的盒子模型

在子元素inner-box我们加了外边距margin,我们从已知的BFC特点知道相邻的BFC外边距会合并。

因为被包裹的inner-box是三个块级元素,在wrap-box内部来说,这三个内部div形成独立的BFC,所以相邻的1-2,2-3的外边距就合并了。

现在我有个需求,我不想让他们合并,我要破坏内部的三个 BFC 结构怎么办?

因此我需要将第二个inner-box改造成一个新的 BFC 结构

<div class="wrap-box">
  <div class="inner-box">1</div>
  <div class="inner-box-2">
    <div class="inner-box">2</div>
  </div>
  <div class="inner-box">3</div>
</div>

注意我在第二个元素多加了一层结构
因此结构变成下面这样,主要看第三个图,我用虚线标出了表明第二元素已经被加了一层结构,貌似外边距还是会合并,这是为啥?

从新的结构我们可以知晓,相邻块级元素的BFC会使边距发生合并,以前的内部的 BFC 是123,现在新的 BFC 是143,2已经被4包裹独立出来了,在 2 内部的margin会作用到父级,从而作用到父级相邻的 BFC 结构。

我们继续在4上添加一个margin:10px 0,神奇的事情发生了,居然还是一样边距被合并了,具体看下代码

.wrap-box {
  width: 300px;
}
.inner-box {
  width: 100px;
  height: 50px;
  margin: 10px 0;
  overflow: hidden;
}
.inner-box:nth-of-type(2n + 1) {
  background-color: red;
}
.inner-box-2 {
  margin: 10px 0;
}


你会发现居然在2的外层加了magrin,居然不会影响整个盒模型的高度。

因此你再细品那句话相邻块级格式上下文的上下边距会产生重叠,于是你恍然大悟,143是三个 BFC 结构,所以 4 设置margin自然就被重合了。

但是我要破坏这种相邻 BFC 结构,因此触发 BFC 结构的机会来了。我给inner-box-2加个样式,用overflow:hidden触发生成一个新的 BFC;

现在就变成了这样了

没错,盒子模型高度变成了190了,中间的4外边距没有合并了。

由于在4不是虽然不是根元素,但是身上加了overflow:hidden触发4形成一个新的 BFC,那么触发 BFC 还有其他什么方式吗?

我们了解到除了overflow:hidden,还有以下几种方式
overflow: auto;display: flex; display: table;display: -webkit-box; float: left;

.inner-box-2 {
  margin: 10px 0;
  overflow: hidden;
  /* overflow: auto; */
  /* display: flex; */
  /* display: table; */
  /* display: -webkit-box; */
  /* float: left; */
}

已经确定的 BFC 不会与相邻浮动的 BFC 边距发生重合

当我们把inner-box-2设置为浮动后,边距就不会合并了。这也证实了相邻 BFC 与已经设置的浮动元素边距并不会合并,但inner-box-2inner-box-1始终在一个大的BFC包裹着,而每一个自身元素又形成一个独立的BFC
:::: code-group
::: code-group-item html

<div class="wrap-box">
  <div class="inner-box inner-box-1">1</div>
  <div class="inner-box inner-box-2">2</div>
</div>

:::
::: code-group-item css

 <style>
    *{
      padding: 0;
      margin: 0;
    }
    .wrap-box {
      width: 300px;
      border: 1px solid #111;
      overflow: hidden;
    }
    .inner-box {
      width: 100px;
      height: 50px;
      margin: 10px 0;
      overflow: hidden;
    }
    .inner-box-2 {
      float: left;
    }
    .inner-box:nth-of-type(2n+1) {
      background-color: red;
    }
    .inner-box:nth-of-type(2n) {
      background-color: yellow;
    }
  </style>

:::
::::

探索 BFC 九宫格布局

我们知道相邻的 BFC 结构垂直方向外边距会合并,利用这点,我们实现九宫格布局
:::: code-group
::: code-group-item html

<div class="wrap-box">
  <div class="inner-box">1</div>
  <div class="inner-box">2</div>
  <div class="inner-box">3</div>
  <div class="inner-box">4</div>
  <div class="inner-box">5</div>
  <div class="inner-box">6</div>
  <div class="inner-box">7</div>
  <div class="inner-box">8</div>
  <div class="inner-box">9</div>
</div>

:::
::: code-group-item css

* {
  padding: 0;
  margin: 0;
}
.wrap-box {
  width: 300px;
  border: 1px solid #111;
  display: flex;
  flex-wrap: wrap;
}
.inner-box {
  width: 100px;
  height: 50px;
  margin: 10px 0;
  overflow: hidden;
  float: left;
}
.inner-box:nth-of-type(2n + 1) {
  background-color: red;
}
.inner-box:nth-of-type(2n) {
  background-color: yellow;
}

:::
::::
注意我们给所有的子元素加了浮动,那么此时会造成父元素高度坍塌,因此父级元素必须要加上overflow:hidden或者设置display: inlie-block或者position: absolute;这样才可以导致父级元素不坍塌。

貌似456中间元素因为设置浮动破坏了BFC,所以我们需要给456设置特殊margin才行,于是乎你给 456 加一层 div,然后设置margin: -10px 0并且要设置左浮动
:::: code-group
::: code-group-item css

.item-2 {
  float: left;
  margin: -10px 0;
}

:::
::: code-group-item html

<div class="wrap-box">
  <div class="inner-box">1</div>
  <div class="inner-box">2</div>
  <div class="inner-box">3</div>
  <div class="item-2">
    <div class="inner-box">4</div>
    <div class="inner-box">5</div>
    <div class="inner-box">6</div>
  </div>
  <div class="inner-box">7</div>
  <div class="inner-box">8</div>
  <div class="inner-box">9</div>
</div>

:::
::::
OK 已经可以了

此时我们这样改 dom 结构似乎有点不是很好,因为可能数据是从后端接口返回并不是写死的数据结构,因此我们再改下结构布局
:::: code-group
::: code-group-item html

<div class="wrap-box">
  <div class="item">
    <div class="inner-box">1</div>
    <div class="inner-box">2</div>
    <div class="inner-box">3</div>
  </div>

  <div class="item">
    <div class="inner-box">4</div>
    <div class="inner-box">5</div>
    <div class="inner-box">6</div>
  </div>
  <div class="item">
    <div class="inner-box">7</div>
    <div class="inner-box">8</div>
    <div class="inner-box">9</div>
  </div>
</div>

:::
::: code-group-item css

* {
  padding: 0;
  margin: 0;
}
.wrap-box {
  width: 300px;
  border: 1px solid #111;
  overflow: hidden;
}
.inner-box {
  width: 100px;
  height: 50px;
  overflow: hidden;
  float: left;
}
.inner-box:nth-of-type(2n + 1) {
  background-color: red;
}
.inner-box:nth-of-type(2n) {
  background-color: yellow;
}
.item {
  margin: 10px 0;
  overflow: hidden;
}

:::
::::
我们最初把margin作用在每个小元素下,现在我们利用BFC的特性,我们把margin作用在item上,因为三个item就是相邻垂直方向的 BFC 结构,边距会产生合并,也正是利用边距合并巧妙的解决了保持边距相等的问题。

具体可以看下效果

由于不同的布局方式,因此写出来的页面拓展性是完全不一样,拓展性强的布局方式,对于后期的维护是相当有益。因此不推荐第一种方式改结构,然后特殊设置456的父边距,虽然效果能达到一致,但是后期维护性与拓展性不高。

BFC 实现自适应布局

有时候左侧固定,右侧自适应这种页面结构时常会有,这种布局方案有哪些可以实现呢
:::: code-group
::: code-group-item html

<h1>左边固定,右边自适应,右边随着左边的宽度而自适应</h1>
<div class="wrap-box">
  <div class="slide-left">left</div>
  <div class="main">main</div>
</div>

:::
::: code-group-item css

* {
  padding: 0;
  margin: 0;
}
.wrap-box {
  width: 300px;
  border: 1px solid #111;
  overflow: hidden;
  resize: horizontal;
}
.slide-left {
  width: 100px;
  height: 100px;
  background-color: red;
}
.main {
  height: 100px;
  background-color: yellow;
}

:::
::::
此时发现页面不尽人意,肯定是下面这样的

但是当我们给slide-left设置float:left后,我们会发现,此时slide-left的文档流被破坏,main会紧贴着slide-left排列

.slide-left {
  width: 100px;
  height: 100px;
  background-color: red;
  resize: horizontal;
  float: left;
}

此时我们可以观察到main贴着slide-left,宽度就是父级的宽度

但实际上main是需要剩下的宽度,他需要根据左侧的slide-left的宽度而自适应
因此你可以,让main成为一个独立 BFC,我们需要设置它oveflow:hidden就行
那么此时就会变成

完整的 css 如下

* {
  padding: 0;
  margin: 0;
}
.wrap-box {
  width: 300px;
  border: 1px solid #111;
  overflow: hidden;
  resize: horizontal;
}
.slide-left {
  width: 100px;
  height: 100px;
  background-color: red;
  float: left;
}
.main {
  height: 100px;
  background-color: yellow;
  overflow: hidden;
}

OK,现在就实现了右侧根据左侧宽度的大小自适应了。

更多关于 BFC 可以参考MDN BFC

总结

  • 了解什么是 BFC,BFC 简称块级格式上下文,它是一块独立的区域影响子元素的排列,相邻区域的 BFC 边距会产生重合

  • 触发 BFC 条件有,display: flexdisplay: inline-blockdisplay:box,position:absolute,或者oveflow: hidden/auto,float:left;

  • 利用 BFC 实现九宫布局,本质利用相邻 BFC 外边距合并

  • 左侧固定,右侧自适应布局

  • 本文 code example

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant