浅析CSS——元素重叠及position定位的z-index顺序
多次在项目中遇到 html 页面元素的非期待重叠错误,多数还是 position 定位情况下 z-index 的问题。其实每次解决类似问题思路大致都是一样的,说到底还是对 z-index 的理解比较模糊,可以解决问题却不大了解其原因,导致重复出错...... 于是决定把重叠问题弄清下,把 z-index 理顺下。
经过一番查找对比实践理解,下面就从元素重叠的背景常识及可能原因说起,浅谈下 position 定位元素的 z-index 顺序。总结下我目前的理解,希望也能对遇到过类似问题有同样疑惑的你有一点帮助或启发。
元素位置重叠的背景常识
(x)html 文档中的元素默认处于普通流(normal flow)中,也就是说其顺序由元素在文档中的先后位置决定,此时一般不会产生重叠(但指定负边距可能产生重叠)。当我们用 css 为某个元素指定 float 浮动或者 position 定位后,元素的定位将会依情况发生如下改变:
1. 指定 float 值 left/right
行内元素也会隐形变成块元素,元素会脱离文档的普通流,向左或右浮动,直到其外边缘碰到包含框或另一个浮动框。
2. 指定 position 值 relative
可以相对于其在普通流中的位置偏移,原本所占的空间仍保留。
3. 指定 position 值 absolute
行内元素也会隐形变成块元素,元素会脱离文档的普通流,相对于最近的已定位祖先元素偏移,如果元素没有已定位的祖先元素,那么它的位置相对于最初的包含块偏移。
4. 指定 position 值 fixed
元素会脱离文档的普通流,相对于浏览器窗口偏移,固定在浏览器的某个位置。
以上四种情况下,文档中的元素都将可能被浮动 / 定位元素覆盖产生重叠。
元素位置重叠的可能原因
1. 负边距 /float 浮动
margin 为负值时元素会依参考线向外偏移。margin-left/margin-top 的参考线为左边的元素 / 上面的元素(如无兄弟元素则为父元素的左内侧 / 上内侧),margin-right 和 margin-bottom 的参考线为元素本身的 border 右侧 /border 下侧。一般可以利用负边距来就行布局,但没有计算好的话就可能造成元素重叠。堆叠顺序由元素在文档中的先后位置决定,后出现的会在上面。
浮动元素会脱离文档的普通流,有可能覆盖或遮挡掉文档中的元素。
2. position 的 relative/absolute/fixed 定位
当为元素设置 position 值为 relative/absolute/fixed 后,元素发生的偏移可能产生重叠,且 z-index 属性被激活。z-index 值可以控制定位元素在垂直于显示屏方向(Z 轴)上的堆叠顺序(stack order),值大的元素发生重叠时会在值小的元素上面。
3. window 窗口元素引发的重叠
浏览器解析页面时,先判断元素的类型:窗口元素优于非窗口元素显示(也就是窗口元素会覆盖在其它非窗口元素之上),同为非窗口类型才能在激活 z-index 属性控制堆叠顺序。
Flash 元素属于 window 窗口元素
所以如果页面上 flash 元素和其他元素发生重叠,需要先将 flash 嵌入的 wmode 属性的 window(窗口,默认的会造成上面所说的问题)改为非窗口模式:opaque(非窗口不透明)或者 transparent(非窗口透明)。
ie6 下 select 属于 window 类型控件
同理,它也产生窗口元素的遮挡问题。解决方法使用 iframe(原理:ie6 下普通元素无法覆盖 select,iframe 可以覆盖 select,普通元素可以覆盖 iframe)/ 用 div 模拟实现 select 的效果。我一般会为被 select 遮挡的 div 在内部追加(appendChild)一个空的子 iframe,设置 position:absolute 脱离文档流空间、width:100%;height:100%; 覆盖整个父 div、z-index:-1; 确保值要小于父 div 的 z-index 值让父 div 覆盖显示在 iframe 上面,借助这个 iframe 来覆盖 select。
浅说 position 定位及 z-index 使用
使用前提
z-index 只能在 position 属性值为 relative 或 absolute 或 fixed 的元素上有效。
基本原理
z-index 值可以控制定位元素在垂直于显示屏方向(Z 轴)上的堆叠顺序(stack order),值大的元素发生重叠时会在值小的元素上面。
使用的相对性
z-index 值只决定同一父元素中的同级子元素的堆叠顺序。父元素的 z-index 值(如果有)为子元素定义了堆叠顺序(css 版堆叠“拼爹”)。向上追溯找不到含有 z-index 值的父元素的情况下,则可以视为自由的 z-index 元素,它可以与父元素的同级兄弟定位元素或其他自由的定位元素来比较 z-index 的值,决定其堆叠顺序。同级元素的 z-index 值如果相同,则堆叠顺序由元素在文档中的先后位置决定,后出现的会在上面。
所以如果当你发现一个 z-index 值较大的元素被值较小的元素遮挡了,请先检查它们之间的 dom 结点关系,多半是因为其父结点含有激活并设置了 z-index 值的 position 定位元素。
也因为这个相对性,还会引发浏览器表现不一致出现兼容问题。原因是 ie6、7 下面 position 值为非 static 的元素在未设置 z-index 值的情况下都会被隐含添加 z-index:0,而 Firefox/Chrome 等现代浏览器会遵循标准默认 z-index:auto 不会产生值。
还有一点需要注意,负值的 z-index 也依照大小比较的原理,但一般来说负值的 z-index 会被透明的 body 覆盖导致点击等事件响应出现问题,请谨慎使用。
百说不如一例,举个例子来简单说明下 z-index
<div class="pr" id="one">
#one 相对定位
<div class="pa pa1">#one 的子元素 pa1,相对 #one 绝对定位,#one 是它的父元素,与.pa2 为同级兄弟元素</div>
<div class="pa pa2">#one 的子元素 pa2,相对 #one 绝对定位,#one 是它的父元素,与.pa1 为同级兄弟元素</div>
</div>
<div class="pa" id="two">#two 绝对定位,与 #one 为同级元素</div>
默认
均未加 z-index 值
.pr{position:relative;}
.pa{position:absolute;}
div{width:200px;height:200px;border:1px solid #ccc;color:#fff;font:bold 14px \5fae\8f6f\96c5\9ed1;}
#one{background:#39f;}
#one .pa1{background:#096;top:25px;left:20px;}
#one .pa2{background:#969;top:90px;left:40px;}
#two{background:#669;top:165px;left:70px;}
表现及解析
定位后依照元素在文档中的先后位置,后出现的会在上面。
相对性试验
为 #one 加上 z-index:1;#one .pa1 加上 z-index:30;#one .pa2 加上 z-index:20;#two 加上 z-index:9;
.pr{position:relative;}
.pa{position:absolute;}
div{width:200px;height:200px;border:1px solid #ccc;color:#fff;font:bold 14px \5fae\8f6f\96c5\9ed1;}
#one{background:#39f; z-index:1;}
#one .pa1{background:#096;top:25px;left:20px; z-index:30;}
#one .pa2{background:#969;top:90px;left:40px; z-index:20;}
#two{background:#669;top:165px;left:70px; z-index:9;}
表现及解析
因为父辈同级元素的 z-index 值 #one<#two,所以#one 决定了其子元素.pa1 和.pa2 的 z-index 值不论有多大都会被#two 所覆盖;作为同级兄弟元素的.pa1 和.pa2 则比较其 z-index 值,较大的.pa1 显示在上面。
ie6、7 兼容性试验
为 #one .pa1 加上 z-index:10;#two 加上 z-index:1;
.pr{position:relative;}
.pa{position:absolute;}
div{width:200px;height:200px;border:1px solid #ccc;color:#fff;font:bold 14px \5fae\8f6f\96c5\9ed1;}
#one{background:#39f;}
#one .pa1{background:#096;top:25px;left:20px; z-index:10;}
#one .pa2{background:#969;top:90px;left:40px;}
#two{background:#669;top:165px;left:70px;z-index:1;}
表现及解析
Firefox/Chrome 等现代浏览器(包括 ie8+)下,父元素 #one 未设置 z-index 值,则默认为 auto,此时的#one .pa1 为自由的定位元素,因此 z-index 较大的 #one .pa1 显示在较小的 #two 上面。如果把#two 的 z-index 值去掉,情况也会是一样的,设置了较大 z-index 值的#one .pa1 会显示在未设置 z-index 的元素上面。
ie6/7 下, 差异在于 #one .pa1 显示在了 #two 的下面。因为对于 ie6/7 父元素#one 未设置 z-index 值,会被隐含设置了 z-index:0; 此时 z-index 值#one 的 0 要与#two 的 1 比较,而#two 比较大,所以#one 的子元素无论 z-index 如何的大也会被#two 遮挡。如果把#two 的 z-index 值去掉,情况依旧,因为未设置 z-index 值的#one 和#two 都会被默认加上 z-index:0; 有了值就可以比较,值相同的情况下堆叠顺序由元素在文档中的先后位置决定,出现在后面的#two 会在上面, 结果#one 的子元素无论 z-index 如何的大还是会被#two 遮挡。
简单总结及建议
普通元素的堆叠顺序由元素在文档中的先后位置决定,后出现的会在上面,请小心计算好浮动和负边距布局,注意窗口元素的特殊性;非同级关系和非父子关系定位元素之间的堆叠顺序,要向上追溯到其为兄弟关系的父元素上,先比较其 z-index 值,只有父辈元素中的 z-index 值较大的后代子元素才能超过 z-index 值较小的父辈元素及其子孙元素。
为了在编码时就减少 z-index 值判断的复杂性,我建议对于一般页面内容类定位元素的 z-index 设置小于 99 的值(如非必要不使用负值),广告类定位元素的 z-index 设置 100~500 的值,公告提示等弹出类定位元素的 z-index 设置大于 500 的值;对于比较复杂定位嵌套页面,为了避免 ie6/7 的显示差异,请为父辈类定位元素显性加上 z-index:0 或其他值。
以上是我目前网络搜寻书籍参考结合实践后的理解总结,如有错误,请不吝赐教;如有疑问,欢迎讨论;如有帮助,万分荣幸;如有雷同,握个手吧~