为何修改CSS filter属性会改变元素层级?如何恢复?
点击Toggle按钮为两个div设置CSS filter的brightness值后,发现元素层级发生了变化。为什么仅修改filter属性会影响层级?该如何恢复原有层级?(当前使用最新版本Chrome浏览器)
示例代码
HTML
<div id="container"> <div><p>Text</p></div> <div></div> </div> <button id="toggle">Toggle</button>
CSS
#container { font-size: 0; font-family: sans-serif; } #container div { background: crimson; width: 100px; height: 50px; display: inline-block; position: relative; font-size: 24px; } p { position: absolute; top: 0; left: 0; margin: 0; line-height: 50px; width: 200%; text-align: center; z-index: 2; } button { margin-top: 10px; display: block; }
JavaScript
const div1 = document.querySelectorAll("#container div")[0]; const div2 = document.querySelectorAll("#container div")[1]; let on = false; document.querySelector("#toggle").addEventListener("click", function() { const value = on ? "brightness(1)" : "brightness(2)"; div1.style.filter = div2.style.filter = value; on ^= true; });
为啥加个filter就搞乱层级了?
其实是CSS里的**堆叠上下文(stacking context)**在搞鬼!当你给元素设置filter属性的时候,浏览器会自动给这个元素创建一个全新的堆叠上下文。
原来的代码里,两个div都是position: relative,但没触发堆叠上下文,所以它们的显示顺序就按HTML里的先后排——第二个div写在后面,所以会压在第一个div上面一点。而那个p元素因为设了z-index: 2,在第一个div的“管辖范围”里是上层,所以能好好盖在两个div上面。
但加了filter之后,每个div都变成了自己堆叠上下文的“老大”。这时候p元素的z-index只能在第一个div的堆叠上下文里管用,没法跑到第二个div的堆叠上下文上面去。而第二个div的堆叠上下文在HTML里更靠后,自然就把p元素给盖住了,看起来就是层级乱了。
如何恢复原有层级?
给你几个实用的解决方案:
方案1:给第一个div设置更高的z-index
既然两个div现在都有自己的堆叠上下文,直接给第一个div加个比第二个div高的z-index,让它的整个堆叠上下文(包括里面的p元素)压在第二个div上面:
#container div:first-child { z-index: 1; }
方案2:让容器统一管理堆叠上下文
给容器#container添加能触发堆叠上下文的属性,比如position: relative配合z-index: 0,这样两个div的堆叠上下文都归容器管,它们的相对顺序和p元素的z-index依然能按预期工作:
#container { font-size: 0; font-family: sans-serif; position: relative; z-index: 0; }
方案3:调整p元素的归属(如果结构允许)
如果能改HTML结构,把p元素移到容器#container下作为直接子元素,这样它就不属于任何一个div的堆叠上下文,能直接盖在两个div上面:
<div id="container"> <div></div> <div></div> <p>Text</p> </div>
对应的CSS调整:
p { position: absolute; top: 0; left: 0; margin: 0; line-height: 50px; width: 200%; text-align: center; z-index: 2; } #container { position: relative; }
内容的提问来源于stack exchange,提问作者DavidsKanal

