關(guān)于z-index的真正問題
關(guān)于z-index的真正問題是,很少有人理解它到底是怎么用。其實(shí)它并不復(fù)雜,但是如果你從來沒有花一定時(shí)間去看具體的z-index相關(guān)文檔,那么你很可能會(huì)忽略一些重要的信息。
不相信我嗎?好吧,看看你能否解決下面這個(gè)問題:
問題:
在接下來的HTML里 有三個(gè)<div>元素,并且每個(gè)<div>里包含一個(gè)<span>元素。每 個(gè)<span>被分別給定一個(gè)背景顏色:紅、綠、藍(lán)。每個(gè)<span>被放置到文檔的左上角附近,部分重疊著其他 的<span>元素,這樣你就可以看到哪些是被堆疊在前面。第一個(gè)<span>有一個(gè)z-index的值為1,而其他兩個(gè)沒有任 何z-index值。
以下就是這個(gè)HTML和它的基本CSS。
HTML代碼
<div>
<span>Red</span>
</div>
<div>
<span>Green</span>
</div>
<div>
<span>Blue</span>
</div>
CSS代碼
.red, .green, .blue {
position: absolute;
}
.red {
background: red;
z-index: 1;
}
.green {
background: green;
}
.blue {
background: blue;
}
關(guān)于z-index的那些事兒
挑戰(zhàn):
嘗試使紅色<span>元素堆在藍(lán)色和綠色<span>的后面,不要打破以下規(guī)則:
不要以任何方式改變HTML標(biāo)記
不要添加/修改任何元素的z-index屬性
不要添加/修改任何元素的position屬性
如果你找到了答案,那么它應(yīng)該像下面這樣:
<div>
<span>Red</span>
</div>
<div>
<span>Green</span>
</div>
<div>
<span>Blue</span>
</div>
div:first-child {
opacity: .99;
}
.red, .green, .blue {
position: absolute;
}
.red {
background: red;
z-index: 1;
}
.green {
background: green;
}
.blue {
background: blue;
}
關(guān)于z-index的那些事兒
解決方案:
這個(gè)解決方法是在第一個(gè)<div>里(紅色<span>的父節(jié)點(diǎn))添加一個(gè)小于1的opacity屬性值。下面就是被添加的CSS的例子:
div:first-child {
opacity: .99;
}
如果你現(xiàn)在很震驚,但是仍然百思不得其解,并且不相信opacity能決定哪個(gè)元素堆在前面,歡迎來社區(qū)提問,當(dāng)?shù)谝淮卧谶@個(gè)問題上被困擾時(shí)我同樣很震驚。
希望接下來的內(nèi)容能夠讓你對(duì)這個(gè)問題更清楚些。
堆棧順序
Z-index看上去如此簡(jiǎn)單:高的z-index堆在低的z-index的前面,對(duì)嗎?這實(shí)際上是錯(cuò)的,是z-index問題的一部分。它看上去如此的簡(jiǎn)單,以至于很多開發(fā)者沒有花相應(yīng)的時(shí)間去讀相關(guān)的規(guī)則。
每一個(gè)在HTML文檔中的元素既可以在其他元素的前面,也可以在其他元素的后面。這就是所謂的堆棧順序。決定這個(gè)順序的規(guī)則被十分清楚的定義在說明文檔中,但是就像之前我已經(jīng)提到過,這些文檔沒有被大多數(shù)開發(fā)者們完全弄明白。
當(dāng)z-index和position屬性不被包括在內(nèi)時(shí),這些規(guī)則相當(dāng)簡(jiǎn)單:基本上,堆棧順序和元素在HTML中出現(xiàn)的順序一樣。(好吧,其實(shí)是有一點(diǎn)復(fù)雜的,但是只要你不使用壓縮邊界來重疊行內(nèi)元素,你可能不會(huì)遇到邊界問題。)
當(dāng)你把位置屬性也包括在內(nèi)介紹時(shí),任何定位元素(和他們的子元素)都在非定位元素前被顯示出來。(說一個(gè)元素被“定位”意思是它有一個(gè)不同于靜態(tài)的位置值,例如相對(duì)的,絕對(duì)的,等等。)
最 后,當(dāng)z-index被提及時(shí),事情變的有點(diǎn)兒復(fù)雜。最初,很自然的假設(shè)帶有高z-index值的元素會(huì)在帶有低z-index值的元素前面,但是后來發(fā) 現(xiàn)沒那么簡(jiǎn)單。首先,z-index只對(duì)定位元素起作用。如果你嘗試對(duì)非定位元素設(shè)定一個(gè)z-index值,那么肯定不起作用。其次,z-index值能 創(chuàng)建堆棧上下文環(huán)境,并且突然發(fā)現(xiàn)看似簡(jiǎn)單的東西變的更加復(fù)雜了。
堆棧上下文
一組具有共同雙親的元素,按照堆棧順序一起向前或向后移動(dòng)構(gòu)成了所謂的堆棧上下文。充分理解堆棧上下文是真正掌握z-index和堆棧順序工作原理的關(guān)鍵。
每 一個(gè)堆棧上下文都有一個(gè)HTML元素作為它的根元素。當(dāng)一個(gè)新的堆棧上下文在一個(gè)元素上形成,那么這個(gè)堆棧上下文會(huì)限制所有的子元素以堆棧的順序存儲(chǔ)在一 個(gè)特別的地方。那意味著一旦一個(gè)元素被包含在處于底部堆棧順序的堆棧上下文中,那么就沒有辦法先出現(xiàn)于其他處于更高的堆棧順序的不同堆棧上下文元素,就算 z-index值是十億也不行!
現(xiàn)在,堆棧上下文有三種方法可以在一個(gè)元素上形成:
當(dāng)一個(gè)元素是文檔的根元素時(shí)(<html>元素)
當(dāng)一個(gè)元素有一個(gè)position值而不是static,有一個(gè)z-index值而不是auto
當(dāng)一個(gè)元素有一個(gè)opacity值小于1
前兩種形成堆棧上下文的方法具有很大意義并且被廣大Web開發(fā)者所理解(即使他們不知道這些被叫做什么)。第三種方法(opacity)幾乎從來沒在w3c說明文檔之外被提及過。
用堆棧順序決定一個(gè)元素的位置
實(shí)際上,為一個(gè)頁(yè)面上的所有元素決定全局堆棧順序(包括邊界、背景、文本節(jié)點(diǎn)、等等)是極度復(fù)雜的,并且遠(yuǎn)遠(yuǎn)超越了本文講述的范圍(再一次,參考文檔)。但是我們最大的目的,就是基本了解這個(gè)順序,它能夠在很長(zhǎng)一段時(shí)間內(nèi)幫助我們提高CSS開發(fā)的可預(yù)測(cè)性。所以,讓我們打破順序,分解為獨(dú)立的堆棧上下文。
在同樣的堆棧上下文里的堆棧順序
下面是幾條基本的規(guī)則,來決定在一個(gè)單獨(dú)的堆棧上下文里的堆棧順序(從后向前):
堆棧上下文的根元素
定位元素(和他們的子元素)帶著負(fù)數(shù)的z-index值(高的值被堆疊在低值的前面;相同值的元素按照在HTML中出現(xiàn)的順序堆疊)
非定位元素(按照在HTML中出現(xiàn)的順序排序)
定位元素(和他們的子元素)帶著auto的z-index值(按照在HTML中出現(xiàn)的順序排序)
定位元素(和他們的子元素)帶著正z-index值(高的值被堆疊在低值的前面;相同值的元素按照在HTML中出現(xiàn)的順序堆疊)
注 解:定位元素帶有負(fù)的z-index值被在一個(gè)堆棧上下文中先排序,這意味著他們出現(xiàn)在所有其他元素的后面。正因如此,它使一個(gè)元素出現(xiàn)在自己父元素之后 成為可能,這以前通常是不可能的事。當(dāng)然,這局限于它的父元素與它在同一個(gè)堆棧上下文,并且不是那個(gè)堆棧上下文的根元素。一個(gè)偉大的例子如Nicolas Gallagher的CSS不用圖像降低陰影。
全局堆棧順序
堅(jiān)定的理解了為什么/什么時(shí)候新的堆棧上下文形成,同時(shí)掌握了同一個(gè)堆棧上下文的堆棧順序,現(xiàn)在讓你來找出一個(gè)特定元素將出現(xiàn)在全局堆棧里的順序不是那么糟糕了吧?
避免錯(cuò)誤的關(guān)鍵是能夠發(fā)現(xiàn)新的堆棧上下文什么時(shí)候形成。如果你對(duì)一個(gè)元素設(shè)置了z-index值為十億但是它沒有在堆棧順序中向前移動(dòng),檢查一下它的祖先樹,看是否它的父節(jié)點(diǎn)形成了堆棧上下文。如果是那樣的話,你的z-index值即使有十億也不會(huì)給你帶來好處。
包扎救治
回到之前的原始問題,我已經(jīng)重建了這個(gè)HTML的結(jié)構(gòu),添加了一些注釋,每一個(gè)標(biāo)簽指明了它在堆棧里的順序。這個(gè)順序是假設(shè)最初的CSS。
<div><!-- 1 -->
<span><!-- 6 --></span>
</div>
<div><!-- 2 -->
<span><!-- 4 --><span>
</div>
<div><!-- 3 -->
<span><!-- 5 --></span>
</div>
當(dāng)我們添加opacity到第一個(gè)<div>,堆棧順序像下面這樣改變:
<div><!-- 1 -->
<span><!-- 1.1 --></span>
</div>
<div><!-- 2 -->
<span><!-- 4 --><span>
</div>
<div><!-- 3 -->
<span><!-- 5 --></span>
</div>
span.red曾經(jīng)的順序是6,但現(xiàn)在改為1.1。我已經(jīng)使用“.”來標(biāo)注一個(gè)新的上下文環(huán)境的形成。span.red現(xiàn)在是那個(gè)新的上下文的第一個(gè)元素。
現(xiàn) 在似乎更清晰了,關(guān)于為什么紅色盒子跑到其他盒子的后面。原始的例子只包含兩個(gè)堆棧上下文,根元素和形成span.red的那個(gè)。當(dāng)我們添加 opacity到span.red的父節(jié)點(diǎn)上,形成了第三個(gè)堆棧上下文,結(jié)果顯示在span.red上的z-index值只能應(yīng)用在那個(gè)新的堆棧上下文 中。因?yàn)榈谝粋€(gè)<div>(應(yīng)用opacity的那個(gè))和它的兄弟元素沒有position或者z-index值的集合,他們的堆棧順序是由 他們?cè)贖TML里的源順序決定的,也就是說第一個(gè)<div>,和它的堆棧上下文里的所有元素被第二個(gè)和第三個(gè)<div>元素分離。