前端面试经典考题:浮动和清除浮动

192 阅读8分钟

前端面试经典考题:浮动和清除浮动

不是水文章,也不是mdn的搬运工,只为分享自己的理解。

浮动作为一种传统的页面布局,在项目中使用相对较少,但是也不排除需要用浮动实现一些特殊的页面效果,同时一些老项目也可能大量使用浮动进行布局。因此,对于浮动我们还是要有一个透彻的理解。

一、浮动

浮动可以实现文字环绕效果、首字下沉效果、块级元素水平排列效果

文字环绕
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    body {
      width: 90%;
      max-width: 900px;
      margin: 0 auto;
      font:
        0.9em/1.2 Arial,
        Helvetica,
        sans-serif;
    }

    .box {
      float: left;
      margin: 15px;
      width: 150px;
      height: 100px;
      border-radius: 5px;
      background-color: rgb(207, 232, 220);
      padding: 1em;
    }

    .special {
      background-color: rgb(79, 185, 227);
      padding: 10px;
      color: #fff;
    }

    .pargraph2 {
      background-color: antiquewhite;
    }
  </style>
</head>

<body>
  <h1>Simple float example</h1>

  <div class="box">Float</div>

  <p class="special">
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla luctus aliquam
    dolor, eu lacinia lorem placerat vulputate. Duis felis orci, pulvinar id metus
    ut, rutrum luctus orci. Cras porttitor imperdiet nunc, at ultricies tellus
    laoreet sit amet.
  </p>

  <p class="pargraph2">
    Sed auctor cursus massa at porta. Integer ligula ipsum, tristique sit amet
    orci vel, viverra egestas ligula. Curabitur vehicula tellus neque, ac ornare
    ex malesuada et. In vitae convallis lacus. Aliquam erat volutpat. Suspendisse
    ac imperdiet turpis. Aenean finibus sollicitudin eros pharetra congue. Duis
    ornare egestas augue ut luctus. Proin blandit quam nec lacus varius commodo et
    a urna. Ut id ornare felis, eget fermentum sapien.
  </p>

  <p>
    Nam vulputate diam nec tempor bibendum. Donec luctus augue eget malesuada
    ultrices. Phasellus turpis est, posuere sit amet dapibus ut, facilisis sed
    est. Nam id risus quis ante semper consectetur eget aliquam lorem. Vivamus
    tristique elit dolor, sed pretium metus suscipit vel. Mauris ultricies lectus
    sed lobortis finibus. Vivamus eu urna eget velit cursus viverra quis
    vestibulum sem. Aliquam tincidunt eget purus in interdum. Cum sociis natoque
    penatibus et magnis dis parturient montes, nascetur ridiculus mus.
  </p>

</body>

</html>

1725510648811.png

浮动元素 (这个例子中的 div 元素) 会脱离正常的文档布局流,并吸附到其父容器的左边(这个例子中的 body 元素)。在正常布局中位于该浮动元素之下的内容,此时会围绕着浮动元素,填满其右侧的空间。

仔细观察一下可以发现:这时第二个段落(以Sed开头的段落)怎么也跑到图片的右边了?实际上并没有跑到右边,根据第二个段落的背景元素可以看到,仍然是按照正常的文档布局流竖向排列。只不过文字被浮动的div元素挤压到右侧了。刚好印证上面说的浮动元素会脱离文档布局流,视觉效果像是浮动元素被提升了一层。

首字下沉

在上一段代码的基础上,给第一个段落添加类名has-dropcap, 利用伪元素选择器,实现首字下沉效果(简单实现,细节可以自行调整):

.has-dropcap::first-letter {
    font-size: 6em;
    float: left;
    margin: 0.1em 0.1em 0.1em 0;
    line-height: 0.65;
}

<p class="special has-dropcap">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla luctus aliquam
dolor, eu lacinia lorem placerat vulputate. Duis felis orci, pulvinar id metus
ut, rutrum luctus orci. Cras porttitor imperdiet nunc, at ultricies tellus
laoreet sit amet.
</p>

1725513113014.png

块级元素横向排列
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    div {
      width: 200px;
      height: 200px;
      float: left;
    }

    div:nth-child(1) {
      background-color: aqua;
    }

    div:nth-child(2) {
      background-color: antiquewhite;
    }

    div:nth-child(3) {
      background-color: aquamarine;
    }
  </style>
</head>

<body>
  <div></div>
  <div></div>
  <div></div>
</body>

</html>

1725513807182.png

二、为什么要清除浮动

高度塌陷

由于浮动元素会默认脱离文档流,因此包含浮动元素的父元素高度不包含浮动元素,这会导致父元素的高度塌陷。

我们修改一下第一段代码中的文档布局,将box盒子和第一个段落包裹在一个类名为wrapper的盒子中

1725514384417.png

此时的蓝色背景就是类名为wrapper的父元素,这个高度实际上与第一个浮动子元素无关,完全由段落撑开。如果此时将wrapper中的段落元素删除掉会是什么情况呢?

1725514504141.png

页面上看不到蓝色了,wrapper元素没有了吗?审查一下元素:

1725514569596.png

可以看到,wrapper盒子的高度为0!这就是最直观的高度塌陷!

我们常常是带着问题找答案,所以容易忽略最初的问题。高度塌陷怎么了?好像也没带来什么问题吧?页面布局看起来也还是正常的吧!用户也不会打开控制台看一下这个元素是不是高度塌陷了。这不是多此一举吗?

举一个经典的例子:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .container {
      padding: 5px;
      background-color: blue;
    }
    
    .item {
      float: left;
      width: 200px;
      height: 200px;
      background-color: aqua;
    }

  .extra {
    width: 200px;
    height: 300px;
    background-color: antiquewhite;
  }
  </style>
</head>

<body>
  <div class="container">
    <div class="item">1</div>
  </div>
  <div class="extra"></div>
</body>

</html>

1725515863208.png

可以看到,出现了意想不到的布局错乱。container元素出现了高度塌陷,兄弟元素以它塌陷的高度进行排版,导致被浮动的item元素覆盖了。

先看一下清除浮动后的效果:

1725516476150.png

  • 清除浮动后的cotainer元素高度 = 200px(item元素的高度) + 5 * 2(上下padding) = 210px;
  • 兄弟元素extra按照文档流正常的向下排列,不存在被浮动元素覆盖问题。没错,这正是我们想要的效果!所以,清除浮动是必要的。
总结

所以为什么要清除浮动呢?总结为以下两点:

(1)防止高度塌陷

当子元素设置了float属性时,父元素可能会无法正确地扩展高度来包含这些子元素。这是因为浮动的元素从文档流中脱离,使得父元素看不到它们的高度。如果不清除浮动,父元素可能会高度为零或仅包含非浮动的内容。这在实际布局中会引发不可预测的显示问题。 清除浮动可以使得父元素的高度得以正确计算

(2)防止布局混乱

如果不清除浮动,非浮动的元素(如文本、图片等)可能会围绕在浮动元素的旁边,或者发生重叠,导致页面布局错乱。

(3)使页面结构更加可控

通过清除浮动,可以确保其他元素正常地在浮动元素之后排列,而不是在旁边或上方。

三、清除浮动

可以给浮动元素下方的第二个段落清除浮动,添加一个cleared类名,使用clear属性清除浮动:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    body {
      width: 90%;
      max-width: 900px;
      margin: 0 auto;
      font:
        0.9em/1.2 Arial,
        Helvetica,
        sans-serif;
    }

    .box {
      float: left;
      margin: 15px;
      width: 150px;
      height: 100px;
      border-radius: 5px;
      background-color: rgb(207, 232, 220);
      padding: 1em;
    }

    .special {
      background-color: rgb(79, 185, 227);
      padding: 10px;
      color: #fff;
    }

    .cleared {
      clear: left;
    }
  </style>
</head>

<body>
  <h1>Simple float example</h1>

  <div class="box">Float</div>

  <p class="special">
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla luctus
    aliquam dolor, eu lacinia lorem placerat vulputate.
  </p>

  <p class="cleared">
    Sed auctor cursus massa at porta. Integer ligula ipsum, tristique sit amet
    orci vel, viverra egestas ligula. Curabitur vehicula tellus neque, ac ornare
    ex malesuada et. In vitae convallis lacus. Aliquam erat volutpat. Suspendisse
    ac imperdiet turpis. Aenean finibus sollicitudin eros pharetra congue. Duis
    ornare egestas augue ut luctus. Proin blandit quam nec lacus varius commodo et
    a urna. Ut id ornare felis, eget fermentum sapien.
  </p>

  <p>
    Nam vulputate diam nec tempor bibendum. Donec luctus augue eget malesuada
    ultrices. Phasellus turpis est, posuere sit amet dapibus ut, facilisis sed
    est. Nam id risus quis ante semper consectetur eget aliquam lorem. Vivamus
    tristique elit dolor, sed pretium metus suscipit vel. Mauris ultricies lectus
    sed lobortis finibus. Vivamus eu urna eget velit cursus viverra quis
    vestibulum sem. Aliquam tincidunt eget purus in interdum. Cum sociis natoque
    penatibus et magnis dis parturient montes, nascetur ridiculus mus.
  </p>

</body>

</html>

1725518042384.png

这样清除浮动没什么问题,却不是我们常见的情况!

如果想让盒子联合包住浮动的项目以及第一段文字,同时让紧随其后的内容从盒子中清除浮动,这就是一个问题。 基础代码片段如下:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    ...

    .wrapper {
      background-color: rgb(79, 185, 227);
    }
  </style>
</head>

<body>
  ...

  <div class="wrapper">
    <div class="box">Float</div>
    <p class="special">
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla luctus
      aliquam dolor, eu lacinia lorem placerat vulputate.
    </p>
  </div>
   
   ...
</body>

</html>
1. clearfix小技巧

先向包含浮动内容及其本身的盒子后方插入一些生成的内容,并将生成的内容清除浮动。

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

1725518700659.png

2. 添加一个额外的div元素并设置clear: both

在浮动盒子后手动添加诸如 div 的 HTML 元素,并设置其样式为 clear:both 。这与第一种方法是等效的。

.clearfix {
	clear: both;
}

<div class="wrapper">
    <div class="box">Float</div>

    <p class="special">
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla luctus
      aliquam dolor, eu lacinia lorem placerat vulputate.
    </p>

    <div class="clearfix"></div>
</div>
3. 使用overflow

一个替代的方案是将包裹元素的 overflow 属性设置为除 visible 外的其他值。

.wrapper {
  background-color: rgb(79, 185, 227);
  padding: 10px;
  color: #fff;
  overflow: auto;
}

大部分情况下这种小技巧都可以奏效,但是可能会出现莫名其妙的滚动条或裁剪阴影,这是使用 overflow 带来的一些副作用。

4. display: flow-root

一个较为现代的方案是使用 display 属性的 flow-root 值。它可以无需小技巧来创建块格式化上下文(BFC),在使用上没有副作用。

.wrapper {
  background-color: rgb(79, 185, 227);
  padding: 10px;
  color: #fff;
  display: flow-root;
}

该属性目前的浏览器兼容情况如图:

1725519531993.png

5. BFC

BFC(块级格式化上下文)也是一个经典考题,且听下集分解...