Skip to content

理解浮动

::: theorem 本章概要
❑ 浮动的工作原理,以及如何避开常见的陷阱
❑ 容器折叠和清除浮动
❑ 媒体对象和双容器模式
❑ 块级格式化上下文
❑ 如何创建和理解一个网格系统 :::

浮动的设计初衷

  • 浮动的创造本意是为了模仿纸媒上常见的文字围绕图片效果
  • 浮动的原意并非用于页面布局,但在过去的很多年,尤其在 Flexbox 和 Grid 面世之前,浮动大多都被用作布局
  • 实现文字围绕图片效果,浮动仍然是唯一的方法
  • 浮动的两种经典用法:页面布局和媒体对象

浮动的实例演示,页面布局和媒体对象:

HTML
<body>
  <div class="container">
    <header>
      <h1>Franklin Running Club</h1>
    </header>
    <main class="main clearfix">
      <h2>Running tips</h2>
      <div>
        <div class="media">
          <img src="001.jpg" alt="" class="media-image">
          <div class="media-body">
            <h4>1 Strength</h4>
            <p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Ut sapiente voluptate velit illo enim, vel Lorem ipsum dolor sit, amet consectetur adipisicing elit. Ut sapiente voluptate velit illo enim
              numquam quos quis libero autem, sapiente voluptate velit illo enim,cumque atque dolores rerum iure! sapiente voluptate velit illo enim,</p>
          </div>
        </div>
        <div class="media">
          <img src="001.jpg" alt="" class="media-image">
          <div class="media-body">
            <h4>2 Cadence</h4>
            <p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Minima quibusdam quod reprehenderit tempore
              suscipit vel cumque doloribus eum aliquid quos optio, maiores sapiente eveniet quis.</p>
          </div>
        </div>
        <div class="media">
          <img src="001.jpg" alt="" class="media-image">
          <div class="media-body">
            <h4>3 Change it up</h4>
            <p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Minima quibusdam quod reprehenderit tempore
              suscipit vel cumque doloribus eum aliquid quos optio, maiores sapiente eveniet quis.</p>
          </div>
        </div>
        <div class="media">
          <img src="001.jpg" alt="" class="media-image">
          <div class="media-body">
            <h4>4 Focus on form</h4>
            <p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Minima quibusdam quod reprehenderit tempore
              suscipit vel cumque doloribus eum aliquid quos optio, maiores sapiente eveniet quis.</p>
          </div>
        </div>
      </div>
    </main>
  </div>
</body>

双容器模式 ( double container pattern ) 通过将内容放置到两个嵌套的容器中,然后给内层的容器设置外边距,让它在外层容器中居中。

在本例中,<body> 就是外层容器。因为它默认是 100% 的网页宽度,所以不用给它添加新的样式。在 <body> 内部,整个网页的内容放在了 <div class="container">,也就是内层容器中。对于内层容器,需要设置一个 max-width,并将外边距设置为 auto,使内容居中。

容器折叠和浮动清除

页面结构为:main 元素 ( 爷爷 ) ,h2 标题 ( 大伯 ) ,匿名 div( 爸爸 ) ,四个 .media 媒体块 ( 孙子 ) 。

  • main 元素 ( 爷爷 )
    • h2 标题 ( 大伯 )
    • 匿名 div ( 爸爸 )
      • .media 媒体块 ( 孙子 )

浮动的是四个 .media 媒体块 ( 孙子 ) ,外边套一个匿名 div 元素 ( 爸爸 ) ,再外面是 main 元素 ( 爷爷 ) 。四个孙子一浮动,不但脱离了爸爸匿名 div 的管控,连爷爷 main 也一起反了。直接领导爸爸匿名 div 塌陷成零高度。爷爷 main 本来掌管着大伯 h2 和爸爸匿名 div,现在爸爸已经塌成零,爷爷 main 的高度只能靠大伯 h2 来支撑了,所以,main 高度仅仅剩下了 h2 标题的一行。

使用浮动进行页面布局,必然会引发外层容器的高度坍塌,但这种行为我们不想要。我们想要的是:四个 .media 孙子浮动出去了,脱离了爸爸匿名 div,也脱离了爷爷 main。爸爸 div 管不了,但是希望爷爷 main 上点手段,可以依然管得住浮动的孙子们。

解决外层容器的高度坍塌 使用跟浮动配套的 clear 属性

【方法一】爷爷派出三叔围堵

此时的网页结构,爷爷 main 下面三个儿子:大伯 h2,爸爸匿名 div,加上新增的三叔 <div style="clear: both"></div>。爷爷果然有手段,凭空生出个三叔,再让三叔略施手段,爷爷的实力范围即刻扩展。这样在浮动孙子的前面有大伯 h2、后面有三叔 div,爷爷高度便给撑起来了,往下可以一直去到三叔的位置。

【方法二】爷爷派出小叔围堵

首先,澄清一种元素关系:伪元素跟宿主元素的关系到底是兄弟关系亦或是父子关系?MDN 文档关于伪元素的解释是:

CSS 中,::before 创建一个伪元素,其将成为匹配选中的元素的第一个子元素。 CSS 伪元素 ::after 用来创建一个伪元素,作为已选中元素的最后一个子元素。

那么根据官方文档,伪元素是宿主元素的子元素,也就是说 ::before 是大儿子,::after 是小儿子。后面所讨论的元素之间的各种层级关系 ( 辈分关系 ) 均是基于这个前提。

CSS
.clearfix::after {
  display: block;
  content: " ";
  clear: both;
}

在本例中,.clearfix 类标签是贴在爷爷 main 身上的,那么 .clearfix::after 逻辑上选中的就是爷爷 main 的小儿子、浮动孙子 .media 的小叔叔 main::after

  • 方法一,爷爷明面上派出一个三叔空白 div
  • 方法二,只需爷爷暗地派出自己的小儿子、浮动孙子的小叔叔 main::after 即可。
  • ::after 的真实辈分关系,要看其前缀 .clearfix 的类标签是贴在谁的身上,贴谁那谁就是伪元素 ::after 的宿主元素 ( 父亲大人 ) 。

接下来,我们来讨论一下原文中关于【方法二】清除浮动的施用对象,此部分内容疑似有误。以下分别是中英文原文摘录。

请注意,要给包含浮动的元素清除浮动,而不是给别的元素,比如浮动元素本身,或包含浮动的元素后面的兄弟元素。 It’s important to know that the clearfix is applied to the element that contains the floats; a common mistake is to apply it to the wrong element, such as the floats or the container after the one that contains them.

先把原文掰开了讲:

  • 清除浮动,要施加在「包含浮动的元素」 ( 爷爷或爸爸 )
  • 清除浮动,不能施加在「浮动元素本身」
  • 清除浮动,不能施加在「包含浮动的元素后面的兄弟元素」 ( 叔叔 )

再来一个个勘误: 上面已经说过了,【方法二】的页面结构:浮动元素是四个媒体块 .media( 孙子 ) ,外边套一个匿名 div 元素 ( 爸爸 ) ,再外面是 main 元素 ( 爷爷 ) 。.clearfix 类标签确实是贴在爷爷 main ( 包含浮动的元素 ) 身上的,但是 .clearfix::after 选中的却是伪元素 main::after,即清除浮动是施加在小叔叔「包含浮动的元素后面的兄弟元素」的身上。由此可见,清除浮动不但可以施加在「包含浮动的元素后面的兄弟元素」 ( 叔叔元素 ) 身上,而且页面采用爷爷、爸爸、浮动孙子三层结构、清除浮动施加在小叔叔 main::after 身上,是最佳实践。

那么清除浮动是否可以施加在「浮动元素本身」上面呢?后文 4.3 小节中的 .media:nth-child(odd) {clear: left} 便是直接把清除浮动施加在了「浮动元素本身」。

【方法三】爷爷派出大伯和小叔

使用浮动布局时,为什么说三层嵌套是最佳实践?因为可以「防止外边距折叠」。 .clearfix 类标签贴在爷爷 main 身上,通过引进 .clearfix::before.clear::after,可将原来的大伯 h2 的外边距限制在爷爷内部。 .clearfix 类标签贴在爸爸 div 身上,则无法限制将大伯 h2 的外边距限制在爷爷内部。

CSS
.clearfix::before,
.clearfix::after {
 display: table;
 content: " ";
}
.clearfix::after {
 clear: both;
}

出乎意料的「浮动陷阱」

浏览器会将浮动元素尽可能地放在靠上的位置。

解决方案:奇数项浮动元素清除浮动。

CSS
.media:nth-child(odd) {
 clear: left;
}

媒体对象和 BFC

布局目标:媒体对象模式 = 左边图片 + 右边文字

HTML 代码结构

HTML
<div class="media">
  <img src="001.jpg" alt="" class="media-image">
  <div class="media-body">
    <h4>1 Strength</h4>
    <p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Ut sapiente voluptate velit illo enim, vel Lorem ipsum dolor sit, amet consectetur adipisicing elit. Ut sapiente voluptate velit illo enim
      numquam quos quis libero autem, sapiente voluptate velit illo enim,cumque atque dolores rerum iure! sapiente voluptate velit illo enim,</p>
  </div>
</div>

第一步,让图片浮动,后面的文字脱离文档流,对图片呈环绕包围状态。

CSS
.media-image {
  float: left;
}

第二步,给文字部分创建 BFC,取消文字的环绕效果,形成媒体对象模式 ( 左图片右文字 ) 。

CSS
.media-body {
  overflow: auto;
}

最终代码:

HTML
<body>
  <div class="container">
    <header>
      <h1>Franklin Running Club</h1>
    </header>
    <main class="main clearfix">
      <h2>Running tips</h2>
      <div>
        <div class="media">
          <img src="04.jpg" alt="" class="media-image">
          <div class="media-body">
            <h4>1 Strength</h4>
            <p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Ut sapiente voluptate velit illo enim, vel Lorem ipsum dolor sit, amet consectetur adipisicing elit. Ut sapiente voluptate velit illo enim
              numquam quos quis libero autem, sapiente voluptate velit illo enim,cumque atque dolores rerum iure! sapiente voluptate velit illo enim,</p>
          </div>
        </div>
        <div class="media">
          <img src="04.jpg" alt="" class="media-image">
          <div class="media-body">
            <h4>2 Cadence</h4>
            <p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Minima quibusdam quod reprehenderit tempore
              suscipit vel cumque doloribus eum aliquid quos optio, maiores sapiente eveniet quis.</p>
          </div>
        </div>
        <div class="media">
          <img src="04.jpg" alt="" class="media-image">
          <div class="media-body">
            <h4>3 Change it up</h4>
            <p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Minima quibusdam quod reprehenderit tempore
              suscipit vel cumque doloribus eum aliquid quos optio, maiores sapiente eveniet quis.</p>
          </div>
        </div>
        <div class="media">
          <img src="04.jpg" alt="" class="media-image">
          <div class="media-body">
            <h4>4 Focus on form</h4>
            <p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Minima quibusdam quod reprehenderit tempore
              suscipit vel cumque doloribus eum aliquid quos optio, maiores sapiente eveniet quis.</p>
          </div>
        </div>
      </div>
      <!-- <div style="clear: both"></div> -->
    </main>
  </div>
</body>
CSS
:root {
  box-sizing: border-box;
}

*,
::before,
::after {
  box-sizing: inherit;
}

body {
  background-color: #eee;
  font-family: Arial, Helvetica, sans-serif;
}

body * + * {
  margin-top: 1.5em;
}

.container {
  margin: 0 auto;
  max-width: 1080px;
}

header {
  padding: 1em 1.5em;
  color: #fff;
  background-color: #0072b0;
  border-radius: .5em;
  margin-bottom: 1.5em;
}

.main {
  padding: 0 1.5em;
  background-color: #fff;
  border-radius: .5em;
}

.media {
  float: left;
  width: calc(50% - 1.5em);
  padding: 1.5em;
  background-color: #eee;
  border-radius: .5em;
  margin: 0 1.5em 1.5em 0;

}

.media:nth-child(odd) {
  clear: left;
}

.clearfix::before,
.clearfix::after {
  display: table;
  content: " ";
}

.clearfix::after {
  clear: both;
}

.media-image {
  float: left;
  margin-right: 1.5em;
}

.media-body {
  margin-top: 0;
  overflow: auto;
}

.media-body h4 {
  margin-top: 0;
}

使用浮动实现的布局仍有小缺陷:第二个 media 灰色块因为文字内容少、缺乏支撑,高度明显偏低了。不符合「自适应式」的大义。请大胆抛弃浮动,拥抱 Flexbox 和 Grid 布局吧。

网络系统

不足:每一行内的两个 cloumn-6 元素,不是等高列,每个元素的高度均由各自的内容撑开,可能一高一低。

总结

❑ 浮动的设计初衷是让文字围绕一个元素排列,但有时这种效果并不是我们想要的 ❑ 使用清除浮动来包含浮动元素 ❑ BFC 有 3 个好处:包含浮动元素,防止外边距折叠,防止文档流围绕浮动元素排列 ❑ 使用双容器模式让页面内容居中 ❑ 使用媒体对象模式将描述文字定位到图片旁边 ❑ 使用网格系统实现更丰富的网页布局