CSS

position属性和z-index属性对页面节点层级的影响总结

最近在做的一个Web项目中因为很多地方都要用tooltip对某些功能和操作做一些信息提示,以便用户可以很容易的理解和使用本系统,由于页面和tips比较多,做的当中遇到了各种奇葩的z-index的问题,也相信很多人在项目中也都多多少少曾因遇到各种不可预期层级遮盖问题而烦恼,所以我决定花点时间来好好研究一些这方面的问题,跟大家分享一下,希望能对大家有所帮助。

同级元素比较:

一.
结论:只设置z-index还没有设置position为relative或absolute或fixed,则z-index是不会起作用的。
示例:
{
A: {z-index:3}
B: {z-index:2}
C: {z-index:1}
}
从上到下的层级关系为:C > B > A
二:
结论:同级元素不设置position,则后面元素会覆盖前面元素
示例:
{
A: {}
B: {}
C: {}
}
从上到下的层级关系为:C > B > A
三:
结论:同级元素之间设置了position(非static)的元素会覆盖没有设置position(或为static)的元素
示例:
{
A: {position:relative;}
B: {}
}
从上到下的层级关系为:A > C
四:
结论:同级元素之间都设置了position(非static),则后面的元素会覆盖前面的元素
示例:
{
A: {position:relative;}
B: {position:relative;}
}
从上到下的层级关系为:B > A
五:
结论:同级元素之间都设置了position(非static),那么设置了z-index(大于0)的元素会覆盖没有设置的元素
示例:
{
A: {position:relative;z-index:1}
B: {position:relative;}
}
从上到下的层级关系为:A > B
六:同级元素之间,各种层级的混合比较,直接看示例:
{
A: {}
B: {position:relative;z-index:2}
C: {position:relative;}
D: {position:relative;z-index:1}
E: {position:relative;z-index:0}
F: {position:relative;}
G: {}
H: {position:relative;z-index:-1}
}
从上到下的层级关系为:B > D > F > E > C > G > A > H

多级元素比较:

一.
结论:父级元素设置了position(非static)或者没有设置的情况下,当子节点没有设置position或只设置了position的情况下,则子节点的层级会受到父节点的影响; 当子节点设置了position(非static)且又设置了z-index(大于0),则子节点的层级不会受到父节点层级的影响
示例1:
{
A: {}
A-1: {}
B: {}
B-1: {}
}
B以及B的所以子元素会覆盖在A以及A的所有子元素
从上到下的层级关系为:B-1 > B > A-1 > A
示例2:
{
A: {position:relative}
A-1: {}
B: {position:relative}
B-1: {}
}
B以及B的所以子元素会覆盖在A以及A的所有子元素
从上到下的层级关系为:B-1 > B > A-1 > A
示例3:
{
A: {}
A-1: {position:relative}
B: {}
}
从上到下的层级关系为:A-1 > B > A
示例4:
{
A: {position:relative;}
A-1: {position:relative;z-index:2;}
A-2: {}
B: {position:relative;}
B-1: {position:relative;z-index:1}
B-2: {}
}

从上到下的层级关系为:A-1 > B-1 > B-2 > B > A-2 > A

二.
结论:父级元素设置了position(非static)和z-index的情况下,则子节点的层级会受到父节点层级的影响,且子节点会一直覆盖父节点的。
示例1:
{
A: {position:relative;z-index:1}
A-1: {position:relative;z-index:2}
A-2: {position:relative;z-index:-100}
B: {position:relative;z-index:3;}
B-1: {position:relative;z-index:4}
B-2: {position:relative;z-index:5}
}
B以及B的所有子元素会覆盖A以及A的所有子元素
从上到下的层级关系为:B-2 > B-1 > B > A-1 > A-2 > A

很多人将 z-index 设得很大, 9999 什么的都出来了, 如果不考虑父节点的影响, 设得再大也没用, 那是无法逾越的层级.

三.【负z-index跟position的关系】
结论1:当父节点设置了position(非static)但没有设置z-index的情况下,子节点如果设置position(非static)和负的z-index值,子节点会被父节点覆盖
{
A: {position:relative;}
A-1: {position:relative;z-index:-1}
}
从上到下的层级关系为:A > A-1

结论2:当父节点设置了position(非static)和z-index的情况下,子节点如果设置position(非static)和负的z-index值,子节点不会被父节点覆盖
{
A: {position:relative;z-index:1}
A-1: {position:relative;z-index:-1}
}
从上到下的层级关系为:A-1 > A

总结

上中下三个大层级(非W3C官方定义,只是个人为了好理解,以下文字总结可能不是太准确,请参照示例来分析):
将没有设置z-index值,或z-index值为0,或z-index值为auto的所有元素归为一类(AAA);
将设置了position(非static)和z-index值大于0的所有元素归为一类(BBB);
将设置了position(非static)和z-index值小于0的所有元素归为一类(CCC);
结论:BBB类的元素会高于AAA和CCC类, AAA类元素会高于CCC类。
示例:
{
A: {position:relative}
A-1: {position:relative;z-index:-20}
B: {position:relative;z-index:2}
B-1: {position:relative;z-index:-10}
C: {position:absolute;z-index:3}
D: {}
E: {position:fixed;z-index:1}
F: {position:fixed;z-index:-1}
G: {position:relative;z-index:0}
H: {position:fixed;z-index:-2}
}
从上到下的层级关系为:B B-1 C E > A D G > A-1 F H

后话:

以上是我本人对position属性和z-index属性的所有可能性情况的分析以及总结。相信很多人也曾遇到过这样的问题:滚动条任你如何滚动,也奈何不了子节点一动不动。 针对这个问题,有时间我会写一篇position属性和scroll属性之间关系的文章跟大家分享一下。

深入理解清除浮动

一、什么是清除浮动?

  • 浮动的缺陷

    • 在了解如何清除浮动之前,先介绍为什么需要清除浮动。如本文开头所说的,浮动虽然可以便于页面布局,但同时会产生一些问题,也就是我们常说的“副作用”。而一个元素设置了浮动(即 float 值为 left, right 或 inherit 并从父元素上继承 left 或 right 值)的常见缺陷是——影响它的兄弟元素的位置和父元素产生高度塌陷,下面对这两个问题展开说明。

    • 一个元素设置了浮动后,会影响它的兄弟元素,具体的影响方式较为复杂,这要视乎这些兄弟元素是块级元素还是内联元素,若是块级元素会无视这个浮动的块框,也就是我们平时看到的效果——使到自身尽可能与这个浮动元素处于同一行,导致被浮动元素覆盖,除非这些 div 设置了宽度,并且父元素的宽度不足以包含它们,这样兄弟元素才会被强制换行;若是内联元素,则会尽可能围绕浮动元素。

    • 另外,浮动的元素脱离了普通流,这样使得包含它的父元素并不会因为这个浮动元素的存在而自动撑高,这样就会造成高度塌陷。

    • 下面是演示效果图:

    • 关于这几点的更多说明,请看 Demo

    • 很显然,无论是影响兄弟元素还是高度塌陷的问题,都不是我们使用浮动的目的,设置浮动,只是为了改变一个元素的布局,但最终的结果却造成了更多不必要的影响,这不利于布局,因此我们需要清除这些额外的影响,也就是本文要介绍的清除浮动,其实更加准确的说,是清除浮动带来的额外影响。

  • 清除浮动的常见方法

    • 了解了为什么要清除浮动后,这里可以开始介绍清除浮动的常见方法了,不过这里并不急于探讨这些方法的原理,首先列出几种常见清除浮动的方法,再作探讨。

    • 说起清除浮动,大家肯定会想起 clear: both ,的确,这是 CSS 中清除浮动的属性,clear 有 both/left/right/none/inherit 几个属性值,分别代表在元素左右两侧不允许出现浮动元素/左侧不允许出现浮动元素/右侧不允许出现浮动元素/不清除浮动/继承父元素的值。

    • 如下图为清除浮动的例子:

    • 也可以看 Demo

    • 从例子中可以看出,设置了 clear: both (当然在该例子中也可以为 clear: left)的元素不会跟浮动元素同行,并且会占据新的一整行,而不是根据内容来自动调整宽度。之所以会这样,要从 clear 的原理说起,clear 会为元素添加足够的空白空间,使到该元素的位置会放置在它前一个浮动元素之下,这跟增加元素外边距使到元素占据满行而强制换行的效果是一样的,事实上在 CSS1 和 CSS2 中,清除浮动正是通过自动为清除元素(即设置了 clear 属性的元素)增加外边距实现的,从 CSS 2.1 开始改为增加额外的空白空间,不改变外边距。现在大家应该清楚了,既然是增加足够的空间使到元素换行,那么最稳妥的办法就是使到该元素占据一整行,也就是 Demo 中的效果。

    • 现在清除了浮动,但是,这只是清除了浮动对于兄弟元素的影响,而高度塌陷的问题还没有解决,因此,我们需要更高级的清除浮动——闭合浮动。

    • 为什么叫闭合浮动?因为浮动的元素脱离了普通流,因此对于它的父元素,它并没有闭合,这时候就需要闭合浮动了。这个问题的解决方法经过多年的发展,已经有了比较完善的方法,下面为大家详细介绍三种常用方法。

    • (1)空 div 方法

      • 这是较为古老的方法了,除了 div ,也有使用其他标签的,但 div 更为适用,因为除了浏览器赋予它的 display: block 外,它没有其他的样式了,也不会有特殊的功能,干干净净。这里插一段题外话,display: block 是浏览器赋予 div 的,存在于浏览器的 user agent stylesheet ,而不是 div 默认 display 的值就为 block ,在 W3C 中,所有的 HTML 标签 display 的默认值都为 inline 。

      • 下面代码中使用到的 box main left aside 为预先设置了相关 CSS 的类,具体可以查看 Demo 的源码,在其他例子中也是如此。

        <div class="box">
          <div class="main left">我设置了左浮动 float: left</div>
          <div style="clear: both;"></div>
          <div class="aside">我是页脚,我的上面添加了一个设置了 clear: both 的空 div</div>
        </div>
        
      • 效果如图:

      • 也可以看 Demo

      • 空 div 方法很方便,但是加入了没有涵义的 div ,这违背了结构与表现分离的原则,并且后期维护也不方便。
    • (2)overflow 方法

      • 在浮动元素的父元素上设置了 overflow 的值为 hidden 或 auto ,可以闭合浮动。另外在 IE6 中还需要触发 hasLayout ,例如为父元素设置容器宽高或设置 zoom:1

        <div class="box" style="overflow: hidden; *zoom: 1;">
            <div class="main left">我设置了左浮动 float: left</div>
            <div class="aside left">我是页脚,但是我也设置了左浮动。</div>
        </div>
        
      • 效果如图:

      • 也可以看 Demo

      • 这个方法相对前者更加方便,也更加符合语义要求,只是 overflow 并不是为了闭合浮动而设计的,因此当元素内包含会超出父元素边界的子元素时,可能会覆盖掉有用的子元素,或是产生了多余的滚动条。这也是在 overflow 方法诞生后依然需要寻找更佳方法的原因。

    • (3)使用 :after 伪元素的方法

      • 该方法来源于 positioniseverything ,结合 :after 伪元素(注意这不是伪类,而是伪元素,代表一个元素之后最近的元素)和 IEhack ,可以完美兼容当前主流的各大浏览器,这里的 IEhack 指的是触发 hasLayout ,具体请看下面的方法。
        <style>
            .clearfix {/* 触发 hasLayout */ zoom: 1; }
            .clearfix:after {content: &quot;.&quot;; display: block; height: 0; clear: both; visibility: hidden; }
        </style>
        <div class="box clearfix">
            <div class="main left">我设置了左浮动 float: left</div>
            <div class="aside left">我是页脚,但是我也设置了左浮动。</div>
        </div>
        
      • 显然,相对来说,这个办法不但完美兼容主流浏览器,并且也很方便,使用重用的类,可以减轻代码编写,另外网页的结构也会更加清晰。

      • 效果如图:

二、清除浮动方法的实质 —— CSS clear 与 BFC 特性

  • 通过上面的例子,我们不难发现清除浮动的方法可以分成两类:

    • 一是利用 clear 属性,包括在浮动元素末尾添加一个带有 clear: both 属性的空 div 来闭合元素,其实利用 :after 伪元素的方法也是在元素末尾添加一个内容为一个点并带有 clear: both 属性的元素实现的。

    • 二是触发浮动元素父元素的 BFC (Block Formatting Contexts, 块级格式化上下文),使到该父元素可以包含浮动元素,关于这一点,下面会为大家进行详细的介绍。

  • BFC 在 CSS 的可视化格式模型 (Visual Formatting Model) 中具有非常重要的地位,很多开发者因为不了解 BFC 的特性而在实际开发中产生很多让人感到莫名其妙的问题。尽管如此,因为 BFC 涉及 CSS 中很少接触的部分,因此国内的相关介绍很少,这里展开说明一下。

  • 相关文章

position属性对overflow的影响, 深入理解position和overflow的关系

项目中遇到了一个问题,就是明明父节点设置overflow:scroll,但是不管滚动条如果滚动,但是子节点一直都不动,因此就小研究了一下position对overflow的影响,跟大家分享一下。

当父节点不设置position情况下,子节点position的四种值的分析:

示例1.1:
body{
A {overflow: scroll;}
A-1 {}
}
效果:A-1会根据A滚动条的滚动而滚动
分析:A-1的默认position设置为static,当position为static时,A-1元素还是遵循正常的文档流,因此A-1会受它父节点属性的影响

示例1.2:
body{
A {overflow: scroll;}
A-1 {position: relative;}
}
效果:A-1会根据A滚动条的滚动而滚动
分析:当A-1的position设置为relative时,A-1元素还是遵循正常的文档流,因此A-1会受它父节点属性的影响

示例1.3:(重点)
body{
A {overflow: scroll;}
A-1 {position: absolute;}
}
效果:A-1不会根据A滚动条的滚动而滚动
分析:当A-1的position设置为absolute时,A-1元素脱离了文档流,所以A-1不再受父节点属性的影响
注意:这时在父节点没有设置position的时,只会受到body节点的影响

示例1.4:
body{
A {overflow: scroll;}
A-1 {position: fixed;}
}
效果:A-1不会根据A滚动条的滚动而滚动
分析:当A-1的position设置为fixed时,A-1元素脱离了文档流,这时A-1只受body元素的影响

当父节点设置position值为非static情况下,子节点position的四种值的分析:

示例2.1:
body{
A {position:relative; overflow: scroll;}
A-1 {}
}
效果:A-1会根据A滚动条的滚动而滚动
分析:跟示例1.1一样,当父节点A设置了position之后,子节点A-1还是遵循正常的文档流,因此A-1会受它父节点属性的影响

示例2.2:
body{
A {position:relative; overflow: scroll;}
A-1 {position: relative;}
}
效果:A-1会根据A滚动条的滚动而滚动
分析:跟示例1.2一样,当父节点A设置了position之后,子节点A-1还是遵循正常的文档流,因此A-1会受它父节点属性的影响

示例2.3:(重点, 注意跟1.3示例对比)
body{
A {position:relative; overflow: scroll;}
A-1 {position: absolute;}
}
效果:A-1会根据A滚动条的滚动而滚动
分析:当父节点A设置了position之后,效果就跟示例1.3不一样了,这时A-1会受到离它自己最近的一个设置了position属性的父节点的影响,再看下面一个示例:
body{
A {position:relative; overflow: hidden;}
A-1 {overflow: scroll;}
A-1-1 {position: absolute;}
}
注意:这时A-1-1不会收A-1的影响,但是会受到A的影响

示例2.4:
body{
A {position:relative; overflow: scroll;}
A-1 {position: fixed;}
}
效果:A-1不会根据A滚动条的滚动而滚动
分析:跟1.4示例一样,当子节点的position属性设置为fixed之后,不管的父节点是否设置了position值,都只会受到body节点的影响,其他任何节点都不会影响它