10081677wc / blog

78 stars 6 forks source link

关于 CSS 中的 float 和 position #26

Open 10081677wc opened 6 years ago

10081677wc commented 6 years ago

关于 CSS 中的 float 和 position

当构建页面排版时,有不同的方法可以使用,使用哪一种方法取决于具体页面的排版要求。在不同的情况下,某些方法可能好过于其他的方法,比如,可以使用若干个浮动元素来构建一个整洁简洁的页面排版;或者,如果需要更复杂的排版要求,可以考虑使用其他方法,比如使用相对定位和绝对定位。

在这篇文章中,我们首先要讨论元素浮动;然后,我们要讨论如何使用 x, y 和 z 轴控制元素的位置。

元素浮动

当构建一个页面的排版时,使用元素浮动是一种直观的方法去控制元素的位置:元素浮动可以让一个元素靠近或者远离另一个元素,它根据一个元素的大小和其父节点容器的大小来构造这个元素在排版中与其他元素之间的关系。当一个元素被浮动时,这个元素是挨着前一个元素,还是出现在新的一行?这取决于它相邻元素的位置。

当然,元素浮动在提供强大威力的同时,也带来了很多问题,一个著名的问题就是:当一个父节点包括了若干浮动的子元素时,父节点的排版会考虑到浮动子元素的大小和位置,但是子元素并不会影响父节点的大小,在这种情况下,父节点的 height 变成了0,并且忽略其他属性。很多时候,这个问题没有被注意到,特别是当父节点没有附合任何的 CSS 样式,而且嵌套的元素也看上去被正确地排列了。如果没有正确地排列嵌套元素,那么可能引发排版上的错误。

请参阅下面的这个例子:类名为 .box-set 的 div 容器应该有浅灰色的背景,但是因为其嵌套元素都是浮动元素,所以原本期望的背景色没有出现。如果你查看 .box-set 的盒模型,你会发现它的 height 值为0。

<div class="box-set">
  <div class="box">Box 1</div>
  <div class="box">Box 2</div>
  <div class="box">Box 3</div>
</div>

.box-set {
  background: #e8eae9;
}

.box {
  background: #8ec63f;
  height: 100px;
  float: left;
  margin: 10px;
  width: 200px;
}

20160318151127703-7.6kB

一个解决方法是在父容器闭合标签前加上一个空元素,并且设置它的 clear:both。“清除浮动元素”这一招可以解决大多数情况,但是它并不是“语义正确”的,随着页面上需要清除的浮动元素的数量增加,需要的空节点的数量也随之增加,但是这些空节点并没有任何的 HTML 语义;

幸运的是,有一些其他技巧同样使用,最著名的就是使用 overflow 和 clearfix 的技巧。

使用 overflow

在父容器上设置 overflow: auto 可以让它自动包含子元素,从而具有了实际的height值,这样上例中的灰色背景将得到显示。在 IE6 中使用这一招时需要给父节点容器设置 height 或者 width 属性,因为实际情况中 height 常常是可变的,所以是设置 width 的值。对于苹果平台上的 IE,设置 overflow: auto 将会给容器增加滚动条,所以最好是设置 overflow: hidden

.box-set {
  background: #404853;
  overflow: auto;
}

20160318151226860-7.9kB

这一招有些副作用:例如,需要增加样式或者移动子元素超出了父容器的范围时,比如实现边框阴影或者加上一个下拉菜单,在下面的例子中你将看到,当边框阴影超出父容器的范围时就会被遮盖了,同时第二个子元素排列有问题。

20160318151247415-21.5kB

使用 clearfix

clearfix 通常搭配 :before 或者 :after 伪元素使用,使用伪元素就是在包含浮动元素的父容器的前面或者后面创造一个隐藏的元素,通过对 :before 伪元素使用 display:table 来创建一个匿名的 table-cell 元素,从而防止子元素的 margin-top 消失;这一招在 IE6 和7中具有一致的效果,同样的,通过设置 :after 伪元素来防止子元素的 margin-bottom 消失。

另外,需要对父容器使用 *zoom 属性触发 hasLayout 机制来解决 IE6 和7的一致性,hasLayout 机制决定了元素应该怎样渲染和框住它的内容,以及元素间的位置关系。

在下面使用 clearfix 的例子中,你将看到元素就算超出了父容器也可以显示了(注意边框阴影):

.box-set:before,
.box-set:after {
  content: "";
  display: table;
}

.box-set:after {
  clear: both;
}

.box-set {
  *zoom: 1;
}

20160318151334682-19.8kB

有效地包含浮动元素

具体使用哪一个技巧取决于你的个人习惯,有些人倾向于使用 clearfix 技巧,因为它在跨浏览器上的一致性更好;另一些人觉得 clearfix 需要更多的代码。因此,选择哪个技巧不重要,重要的是注释注明和写进文档,一个常见的实现就是给所有需要包含浮动元素的父容器一个统一的类名,方便管理样式:比如使用下面的代码,这样只需给需要包含浮动元素的父节点加上 group 的类名即可。

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

.group:after {
  clear: both;
}

.group {
  *zoom: 1;
}

Position 属性

有些情况下,你需要更多的对元素位置的控制,这时候就需要使用 position 属性了,position 属性一共有五种不同的取值:

Position Static

这是 position 属性的默认取值,意味着这个元素没有也不接受任何对于该元素的位置偏移量属性,在下面的例子中,所有的盒子都将一个接一个的堆起来,因为它们都是块级元素,并且没有被浮动。

<div class="box-set">
  <div class="box box-1">Box 1</div>
  <div class="box box-2">Box 2</div>
  <div class="box box-3">Box 3</div>
  <div class="box box-4">Box 4</div>
</div>

20160318151444517-13.5kB

Position Relative

relative 定位非常相似于 static 定位,最大的区别就是相对定位的元素可以使用偏移量:top,right,bottom,left,这四个偏移量可以让元素朝任何方向移动,从而精确的控制显示出来的位置。

注意,尽管相对定位的元素可以使用偏移量属性,但是它在网页排版时按照的却是跟 static position 相同的方式(这点区别于 fixed position 和 absolute position);另外,相对定位的元素可以覆盖其它元素,或者被其它元素覆盖,尽管它自身并没有偏移。

在下面的例子中,所有的盒子都是一个接一个的堆起来,但是他们按照各自被定义的 offset 相对于原本的位置做了改变,所以,有些盒子覆盖了其他的盒子,而不是推开其他盒子;如果一个元素是相对定位(甚至它还做了偏移),它周围的元素在排版时参考的依然是那个元素原本的位置(指在没有偏移时的位置)。

<div class="box-set">
  <div class="box box-1">Box 1</div>
  <div class="box box-2">Box 2</div>
  <div class="box box-3">Box 3</div>
  <div class="box box-4">Box 4</div>
</div>

.box-set {
  background: #e8eae9;
}

.box {
  background: #8ec63f;
  height: 80px;
  position: relative;
  width: 80px;
}

.box-1 {
    top: 20px;
}

.box-2 {
  left: 40px;
}

.box-3 {
  bottom: -10px;
  right: 20px;
}

20160318151521841-16.2kB

如果同时声明 top 和 bottom 的值,那么 top 值的优先级更高;如果同时声明 left 和 right 的值,那么优先级取决于网页的语言(例如,英语法语德语西班牙语):比如,在英语的网页中,left 的优先级更高;在阿拉伯语的网页中,right 的优先级更高。

Position Absolute

绝对定位的元素接受偏移量,在排版中,绝对定位的元素从原来的位置上被抹去了(因此它后面的元素将占领它目前的位置),然后根据它的相对定位的父节点的位置定位,如果它没有相对定位的父节点,那么元素直接参考 body 容器定位。如果没有对绝对定位的元素使用偏移量属性,那么该元素将被放置在父节点的左上角;如果只设置了 top 属性,那么该元素则只做垂直方向的偏移,而水平方向依然靠着父节点的最左边。

在下面的例子中,所有的盒子相对于父容器作了绝对定位:如果偏移量是正数,那么盒子被“往里推”了;如果偏移量是负数,那么盒子被“往外拉”了。

<div class="box-set">
  <div class="box box-1">Box 1</div>
  <div class="box box-2">Box 2</div>
  <div class="box box-3">Box 3</div>
  <div class="box box-4">Box 4</div>
</div>

.box-set {
  background: #e8eae9;
  height: 200px;
  position: relative;
}

.box {
  background: #8ec63f;
  height: 80px;
  position: absolute;
  width: 80px;
}

.box-1 {
  top: 6%;
  left: 2%;
}

.box-2 {
  top: 0;
  right: -40px;
}

.box-3 {
  bottom: -10px;
  right: 20px;
}

.box-4 {
  bottom: 0;
}

20160318151630237-15.4kB

如果绝对定位的元素没有固定的height或者width值:

如果元素并没有固定 height 值,可是却同时有 top 和 bottom 的值,那么该元素将横跨除了 top 和 bottom 之外剩下的所有高度;同理,如果元素并没有固定 width 值,可是同时有 left和 right 的值,那么该元素将横跨除了 left 和 right 之外剩下的所有宽度;如果元素既没有固定 height 和 width 值,却同时有 top,bottom,left,right 值,那么该元素将占据除了四条边距离之外的所有空间.

Position Fixed

固定定位的元素跟绝对定位的元素很相似,只不过它参照的坐标系是浏览器的视口(viewport),固定定位的元素不随着页面滚动而移动,它会始终保持在那个位置;另外,在 IE6 中使用固定定位有一个 bug ,需要使用一些 hack 来解决。一个典型的使用固定定位的例子是网页中的顶部横条或者底部横条:当用户滚动页面时,那个横条始终停留在浏览器视口的顶部或者底部。

<footer>Fixed Footer</footer>

footer {
  bottom: 0;
  left: 0;
  position: fixed;
  right: 0;
}

z-index 属性

网页是一个二维空间,根据 x 轴和 y 轴展示各个元素。可是,如果你需要让某个元素放置在其他元素的上面,这时你可以改变其 z-index 属性,元素在 z 轴的位置的位置取决于它在 DOM 中出现的先后顺序:如果两个元素发生重叠,先在 DOM 中出现的元素通常排在后出现元素的下面,改变先出现元素的 z-index 属性是一个简单的方法让它出现在更上面,拥有最高 z-index 属性的元素永远被排在最上面,与它在 DOM 树中的位置无关。如果你需要设置一个元素的 z-index 属性,你必须首先设置这个元素是 relative,absolute 或者 fixed 定位,就像对于设置 top, bottom, left, right 属性一样;

<div class="box-set">
  <div class="box box-1">Box 1</div>
  <div class="box box-2">Box 2</div>
  <div class="box box-3">Box 3</div>
  <div class="box box-4">Box 4</div>
</div>

.box-set {
  background: #e8eae9;
  height: 160px;
  position: relative;
}

.box {
  background: #8ec63f;
  border: 3px solid #f7941d;
  position: absolute;
}
.box-1 {
  left: 10px;
  top: 10px;
}

.box-2 {
  bottom: 10px;
  left: 70px;
  z-index: 3;
}

.box-3 {
  left: 130px;
  top: 10px;
  z-index: 2;
}

.box-4 {
  bottom: 10px;
  left: 190px;
  z-index: 1;
}

20160318151818222-10.7kB