CSS浮动简介以及实现

1,918 阅读15分钟
原文链接: www.zcfy.cc

你从来没了解过的CSS浮动

浮动到底是做什么呢?他们是如何影响相关元素的盒模型的呢?浮动的元素与内联元素有什么不同呢?制定浮动元素的位置的具体规则是什么?clear属性是如何工作的,并且它的作用是什么?

即使是经验丰富的开发者也会在浮动上出错,所以理解浮动的行为能帮你摆脱面对CSS的很多困扰。即使你认为你已经了解了关于浮动的所有知识,我们也会深入到你可能会学习到新知识的地步。

什么是浮动?

在CSS中一些元素是块级元素,他们会自动启用新的一行。例如,如果你创建2个单独的单词段落元素,它们不会相互流入而会各自出现在单独的一行。另一种元素是内联元素,它们会与之前的内容保持显示在“一行”。举个例子,有个锚标签,它可以出现在像段落这个元素之内而且不会造成任何额外的空白,也不会另起一行。

改变这种布局模型的一种方式是使用浮动,它允许给定的元素挪动到它那一行的一侧,并且其他内容向下流动。一个右浮动的元素将被推动直到它的容器的右侧,并且内容会沿着它的左侧向下流动,一个有浮动的元素会被挪动到左侧,内容会沿着它的右侧向下流动。

有一个经典的例子是当你将一张图片放进一段落里,并且想让两者并排出现而不是堆叠。首先,我们用HTML创建两个元素:

<img src="http://lorempixum.com/200/200/" />
<p>Lorem ipsum...</p>

仅仅这样并不能出现我们想要的效果。段落元素是一个块级元素,它会独自占一行,所以段落和图片会显示在正常的文档流。

screenshot

我们可以通过将图片浮动到右边来改变显示。这种CSS非常基础:

img {
    float: right;
    margin: 20px;
}

有了这段代码,我们的图片会被挪到它这一行的右边,段落文字会在它的左边向下流动。点击 here 或者点击下面的图片来查看并修改该代码的实际运行实例。

screenshot

有趣的是,这张图片在被浮动时,我们其他的内容将会尽可能的尝试围绕它出现。如果改变我们容器的大小或者将浏览器窗口变窄,文本只是简单的自我重排而不会触碰到图片。

screenshot

这种盒子是怎么工作的

可能你对这种行为已经有了很好的理解。然而,为了更充分利用浮动,你需要更深层次的理解这两个元素之间的交互。例如,我们怎么在段落和图片之间添加额外的空白?你可能认为这样可行

p {margin: 20px;}

然而,这不会在图片和段落之间产生甚至一像素额外的空白。相反,我们为图片应用了这些空白:

img {margin: 20px;}

screenshot

你应该问自己一个问题,“为什么?”为什么增加段落的margin不会在图片和段落之间增加空白?因为我们没有掌握属于段落的盒模型。

如果你曾怀疑你的布局是怎么在基本层面上工作的,试着应用一两个边框来看看将发生什么。如果我们在这个段落上这样做,结果可能会让你惊讶,

p {
    border: solid 1px black;
}

screenshot

正如你所看到的,图片实际上在段落的盒子里面!这解开了我们的边界空白疑惑。我们给段落添加的任何空白都被应用到了图片的右边,这就是为什么在图片和段落之间不会增加空白。

如果我们想改变这种行为,让段落不再围绕图片,我们可以让段落浮动到左边,并给他指定宽度(没有表示宽度,段落将会是100%的宽,将不会布置在图片旁边)。

img {
  float: right;
  margin: 20px;
}

p {
  float: left;
  width: 220px;
  margin: 20px;
}

screenshot

疯狂的浮动规则

现在我们知道了浮动是做什么的,以及它是怎么影响所涉及的元素的盒子的。让我们继续讨论另一块可能很多开发者都不了解的信息:控制浮动元素位置的规则。

通常在图片集或者特征列表中开发人员会用浮动来控制列表项的位置。我们创建一个简单的纯图片的列表来看看这是怎么工作的。

<ul>
  <li><img src="http://placehold.it/100x100&text=1"/></li>
  <li><img src="http://placehold.it/100x150&text=2"/></li>
  <li><img src="http://placehold.it/100x100&text=3"/></li>
  <li><img src="http://placehold.it/100x100&text=4"/></li>
  <li><img src="http://placehold.it/100x100&text=5"/></li>
  <li><img src="http://placehold.it/100x150&text=6"/></li>
  <li><img src="http://placehold.it/100x100&text=7"/></li>
</ul>

默认情况下,所有的列表项都显示在一个大的垂直栈里,这意味着它们是块级元素。即使图片是内联元素,它们也被它们的父块级元素列表项控制。为了解决这种问题,我们要使列表项左浮动。当一行中多个元素被浮动,它们会产生同内联元素的流类似的效果。然而,正如我们所看到的,有一些关键的区别。

li {
    float: left;
    margin: 4px;
}

现在,如果我们所有的图片都是同样的高度,这只是一个非常普通的例子。这个结果看起来就像是一个简单的从左到右按顺序排列的图片集:

screenshot

然而,我们的图片并不是同样的高度,一些高100px,其他的高150px。这会造成非常古怪的结果,点击这里或者下面图片来查看实际效果。

screenshot

我第一次看到这种结果的时候感到很困惑。这里的世界到底发生了什么?为什么图片4会像那样在右边?难道它不应该自己试着尽可能让自己浮动到左边?如果我们去掉这些列表项的浮动,用display:inline来替代,结果是截然不同的。

li {
  display: inline;
}

screenshot

跟最开始相比,这个例子的不同之处在于图片的默认状态是沿着它们的底部边缘垂直对齐。这会使它们同之前的例子看起来非常不一样,但是我们可以使用一行CSS来解决这个问题:

img{
    vertical-align: top;
}

现在看起来比较像浮动示例了,只会显示有一个可预测顺序的内联列表项。当x轴没有下一项的空间时,它会返回到下一行的左侧。

screenshot

所以,我们的浮动图片集为什么没有像这样显示呢?为什么浮动的列表项被奇怪的巫术控制?

翻译要求

事实证明,CSS规范列出了九条规则来规定浮动的行为。这个清单的问题在于即使是被写出来了,也只有律师或者无聊的人才能去理解它。这里是一个从其中一个规则中引用的:

“If the current box is left-floating, and there are any left-floating boxes generated by elements earlier in the source document, then for each such earlier box, either the left outer edge of the current box must be to the right of the right outer edge of the earlier box, or its top must be lower than the bottom of the earlier box. Analogous rules hold for right-floating boxes.”

也许你的阅读理解能力比我要强,但是这个还有其他的规则都让我头晕。所有说什么左外边缘在右外边缘的右边的言论都是非常平常的东西,却被装扮得听起来十分复杂。为了你们的方便,Josh Johnson将浮动表现翻译成英文并总结了九条规则,看起来更简单一点。

  1. 浮动元素会被推到他的容器的边缘。

  2. 任何浮动元素都会出现在他之前的浮动元素的旁边或是下方。如果元素都是左浮动,那么第二个元素将会出现在第一个元素的右边,如果都是右浮动,第二个元素会出现在第一个元素的左边。

  3. 左浮动的盒子不能出现在右浮动盒子的右边。

  4. 浮动元素不能高过他的容器的上边缘(当涉及到塌陷的边距这将会更复杂,请参考最初的规则)。

  5. 浮动元素不能比前一个块级元素或浮动元素高。

  6. 浮动元素不能高过前一行内联元素。

  7. 靠着另一个浮动元素的浮动元素不能超出自己的父容器边缘。

  8. 一个浮动的盒子必须尽可能的高的放置。

  9. 一个左浮动的盒子必须尽可能左的放置,一个右浮动的盒子要尽可能的右的放置。尽可能高的位置的优先级比左右高。

我们可以看做这些大多数都是常识,但他们也要被明确声明出来以便每个人在每个浏览器上看到同样的页面。基本上来说,这种情况的要点就是浮动元素会被指定到具体的边缘(左或者右),没有过多的要求。除非在他之前有另一个浮动元素,他就会与那个浮动元素相邻。

真正让我们感到困惑的是最后的规则,它表明了浮动元素尽可能的宝石高的位置,并且垂直定位规则比水平的左右规则在将元素推到边缘时优先级更高。

在我们之前的例子中,数字2的图片把行的高度撑开了,以至于在数字3图片之后仍然有一些垂直的空间使得数字4图片挤了进去。即使是考虑到这些规则,这种模式也不容易被预测到。

请记住,当你有一个浮动元素时,后面紧接着的浮动元素至少要占据与之前同样或者更多的垂直高度来打破这一行使得流动下移。

screenshot

浮动顺序

关于我们在这里提出的规则的最后一点。第2条规则会对那些浮动元素产生有趣的影响。假设,我们有六张从一到六的数字图片,如下:

<ul>
  <li><img src="http://placehold.it/100x100&text=1"/></li>
  <li><img src="http://placehold.it/100x100&text=2"/></li>
  <li><img src="http://placehold.it/100x100&text=3"/></li>
  <li><img src="http://placehold.it/100x100&text=4"/></li>
  <li><img src="http://placehold.it/100x100&text=5"/></li>
  <li><img src="http://placehold.it/100x100&text=6"/></li>
</ul>

如果我们将这些图片浮动到左边,他们会按顺序出现,从一开始直到六,从左到右,从上到下。但是,如果我们将这些图片右浮动。

screenshot

正如你所看到的,第一张图片被放到了最右边的位置。类似的,在换行时,第四张图片也被放置到了右边。这就是为什么你很少看到有人将导航元素右浮动。弄出了这样螺旋状的顺序就需要HTML结构做出不受欢迎的改变来解决。

清除浮动

浮动对于完成一些类似于创造内容列这样的布局很有用。但是,一旦浮动被声明,他们就会对剩下的文档的产生一些或许你喜欢或者不喜欢的影响。例如,我们想在我们之前提及的那种左浮动的列表块之后加一个段落。

screenshot

结果或许并不是你所期望的:

screenshot

这里的结果是使用clear属性,让没有浮动的元素可以出现在他所应用的元素给定的一侧。例如,比如我们在我们的小图库列表的第二项添加clear: left.

ul li:nth-child(2) {
  clear: left;
}

这段代码告诉浏览器,第二项的顶部必须位于他之前的任意左浮动项的底部之下(在这个例子中,是第一项)。如果我们将所有元素都右浮动,我们就要使用clear: right

screenshot

请注意,在此之后,剩下的浮动元素都会保持他们的位置。因为他们仍然被设置的左浮动,清除属性并没有取消他们的表现。这意味着我们的问题不能通过清除列表任意项的浮动来解决。

screenshot

相反,必须被清除的是段落元素,他是一个没有被浮动的块级元素。这将确保他会出现在浮动元素的下面而不是旁边。

p {
    clear: both;
}

从技术上来讲,我们只需要清除这里的左浮动,但是当一个开发者想要确保清除所有浮动时,通常会看到clear的值被设置为both。这个变化能非常好的解决我们的问题。

screenshot

浮动的怪癖和清除浮动

当一个给定的元素只包含浮动元素时会产生奇怪的现象:父元素的高度会坍塌。举例说明,假设我们想在所有示例中的无序列表给定背景色。如果列表的元素没有被浮动,我们就只需要应用一下CSS来添加背景。

ul {
    background: gray;
}

正如你所看到的,定义的无序列表的框已经变成了灰色,并且列表中的每一项都堆叠在一起。

screenshot

然而,在第二个例子中,我们浮动了所有列表项,UL只包含了浮动的元素,导致它的高度坍塌,这会让新手开发者惊讶它的背景色到底发生了啥。

screenshot

有一些方法可以解决这个问题,最简单容易的方法是为无序列表的父元素明确指定高度。

ul {
  height: 300px;
}

screenshot

正如你所看到的,这的确能让我们的背景重新被填满。然而,从长远来看这并不是一个可取的办法,如果高度是基于内容自动计算的话。如果我们要添加三个或者更多的图片到列表库里,高度就会不足。

为了解决而清除浮动

这里术语clear fix,也叫clearfix,就开始发挥作用啦。清除浮动通过使用clear属性来解决高度坍塌的问题。

开发者在他们的HTML中创建一个跟浮动元素同级的空元素(常常是一个div),然后给空容器添加一个命名为clearfixclass。我们回到CSS中,给clearfix添加清除浮动的属性。

.clearfix {
  clear: both;
}

立刻就解决了高度坍塌的问题:

screenshot

根据我们已经学过的可以知道为什么这个方法可以解决我们的问题。高度坍塌的原因是因为父元素只包含了浮动元素的,现在他有了一个子元素,尽管这个子元素是空的,但是它没有浮动,所以高度会再次跟预期一样自动生成。

这个方法有个问题是,没有人喜欢HTML中额外的丑陋的元素。它根本不是语义的,意味着它不能清晰的传递页面的层次结构。

新的解决方案是利用overflow属性,这个属性控制了超出其包含框边界的内容的功能。如果你讲父项目的overflow设置为hidden或者auto,也能解决高度坍塌。

ul {
  overflow: auto;
}

screenshot

这绝对是解决高度坍塌问题的最简单、最优雅的方案,也应该是你的方案。虽然这么说,那在某种情况,你想讲元素的overflow设置为visible,你又应该怎么做呢?

这样的结果可以使用Nick Gallagher的微型清除浮动hack,他使用了一下天才般的CSS来解决这个问题。首先,他是使用的:before以及:after来添加一些内容,我们可以用来为父元素创建一些不浮动的元素。然而,你不想在这里使用真实的内容,所以我们使它为空并且设置displaytable来创建一个匿名框(空的而且不占空间)最后使用我们的老朋友clear。它创建了一个不可见的块级元素来解决高度坍塌的问题并且没有添加额外的HTML。IE的老版本需要自己修复。

/* For modern browsers */
.cf:before,
.cf:after {
    content:"";
    display:table;
}

.cf:after {
    clear:both;
}

/* For IE 6/7 (trigger hasLayout) */
.cf {
    zoom:1;
}

##结论

文章中,我们讨论了大量的信息,包括基础和复杂的。我们从最开始讨论如何浮动,以及他们在基本层级是如何工作的,然后是如何设置元素的浮动来影响元素所涉及的盒子边框,让你找出怎么样工作才能根据你想的那样获得边距。

接着,我们讨论了浮动元素的基本规则,并得出了一些有趣的结论,就是不同高度的浮动元素如何定位,以及右浮动的元素怎么以相反的顺序出现。

如果在阅读本篇文章之前,浮动让你感觉到很困惑,那么开始阅读吧。它们最开始就迷惑了我们。希望你现在以及了解了浮动是怎么工作的,以及知道如何使用它们来实现你想要的布局。如果你觉得这些信息有帮助,请在下面留下评论让我们知道。