携手创作,共同成长!这是我参与「日新计划 8 月更文挑战」的第7天,点击查看活动详情
css堆叠上下文
这个不知道你在业务中有没有遇到过,前段时间搭建vuepress1.0
就遇到这样的一个类似的问题,主要是用了vuepress-reco
这个主题,去官方提了一个issue[1],自己提的issue
最后自己找到原因了,但是还是有小伙伴遇到同样的类似问题,今天一起探讨一下这个css堆叠上下文
的问题
正文开始…
fixed失效了
我们直接用具体案例来体会css堆叠上下文
,因为官方veurpess-reco1.x
版本,当你开启右侧子菜单时,右侧的子菜单fixed
就失效了。
我们具体写个例子分析下
<divid="app">
<divclass="wrap">
<divclass="subContent">我是fixed在最右侧</div>
<divclass="inner-content"></div>
</div>
</div>
对应的css
如下
*{
padding:0;
margin:0;
}
.wrap{
height:300px;
border:1pxsolid#111;
margin:10px;
overflow-y:auto;
}
.subContent{
position:fixed;
right:10px;
top:20px;
background-color:red;
}
在js
中我生成了50
条数据
functionrenderHtml(){
constinnerContent=document.getElementsByClassName("inner-content")[0];
letstr="";
letmax=50;
for(leti=0;i<max;i++){
str+=`<p>${i}</p>`;
}
innerContent.innerHTML=str;
}
renderHtml();
我们知道我给了.subContent
的样式是fixed
,fixed
是相对整个body
的,所以此时当你滚动内容时,会一直固定在最右侧
但是恰巧,此时遇上了一个问题fixed
失效了,也正是一行css
的原因导致的
.wrap{
height:300px;
border:1pxsolid#111;
margin:10px;
overflow-y:auto;
transform:translateY(0);
}
由于父级.wrap
设置了transform
导致子级subContent
的fixed
失效了
fixed
失效了,所以就是这个父级元素设置的transform: translateY(0)
造成的
为了解决这个问题,我们重置了该样式,将其改成了transform:none
,于是fixed
就正常了,这也是在解决vuepress-reco1.x
主题右侧子菜单fixed
失效的原因。
什么样情况会造成fixed
失效
除了父级设置transform
不为none
,还有filter
不为none
也会造成fixed
失效
.wrap{
height:300px;
border:1pxsolid#111;
margin:10px;
overflow-y:auto;
transform:scale(0.5);
/*filter:blur(1px)*/
}
堆叠上下文
参考张鑫旭老师的一篇博文深入理解CSS中的层叠上下文和层叠顺序[2],参照张鑫旭老师的一张图,大概就是这样
就是我们看到网页上显示是二维的,实际上还有三维,就是一个类似控制transform:translateZ
的一个概念
我们只知道在网页中所有可见元素都是由标签组成,所有标签的排列布局其实是由一个经典的概念构成块级格式上下文
也俗称BFC
,所以整个网页的布局是由BFC
这样的特性而构建我们的网页
看一个例子
<divclass="wrap2">
<divclass="leavel-1">leavel-1</div>
<divclass="leavel-2">leavel-2</div>
</div>
.wrap2{
margin:10px;
}
.leavel-2,.leavel-1{
width:100px;
height:100px;
}
.leavel-1{
background-color:red;
}
.leavel-2{
background-color:green;
}
就会下面这样
正常情况参照BFC
特性,两个块级元素就是这样独占一行的排列了,但是如果我给两个元素设置浮动
.leavel-1,.leavel-2{
float:left;
}
此时发现就会像下面这样
然后我再设置.leavel-2
的margin-left: -100px
,你就会发现leavel-1
被挡住了
初学者可能会好奇,也很容易想到,这leavel-1
去哪里了,实际上是在leavel-2
的下级,我们把leavel-2
的宽度调整一下
隐藏出来的.leavel-1
九显示出来了
所以你现在明白了层叠上下文
了吗,简单的说,网页的所有元素可以像盖棉被一样,一层一层的往上盖,最新的叠在最上面
我们思考下,从浏览器默认的BFC
结构到我们想要看到的堆叠上下文
的效果,这中间我们主要做了哪些事情
1、设置了浮动【破坏了文档流】
2、设置.leavel-2
的外边距margin-left:-100px
【改变了元素的默认排列位置】
所以产生堆叠上下文
,必须满足两个条件,一个是原素文档流被破坏,二是元素位置发生变化
定位产生堆叠上下文
其实除了这浮动+margin
方式,其实我们还可以用定位
去产生堆叠上下文,但实际上也是满足这两个基本的条件
但是如果是用定位,那么有个z-index
这个属性是可以影响层叠上下文的顺序的,z-index
越小,排得越下面
transform产生堆叠上下文
我们发现浮动+margin
,position
能产生上下文,除了这两个,新增的css3
最新特性中还有transform
也可以产生堆叠上下文
因此我们可以这么做
.leavel-2,
.leavel-1{
width:100px;
height:100px;
}
.wrap2{
margin:10px;
}
.leavel-1{
background:red;
}
.leavel-2{
background-color:green;
transform:translateY(-100px);
}
我们会发现此时leavel-2
就把leavel-1
完全盖住,因此transform
也可以产生堆叠上下文,但实际上这个特性并不是像前面两个一样,并不会破坏文档流,所以这是一个例外,他只是改变自身位置,从而形成了堆叠上下文
堆叠优先级问题
我们看到元素,优先级行内元素是不是最高,比如元素的内容文字,永远在最顶层,然后就是背景,然后就是z-index
设置的可见元素
当一个元素同时设置定位
与transform
,影响层叠上下文是怎么样
我们看到fixed
会比transform
的优先级更高,如果去掉transform
,就是就是贴着body
排的
所以这就证明,浏览器在处理层叠上下文
优先级就是先执行定位
,然后再执行transfrom
,这只是作用在同一个元素上
回到我们刚开始的问题上,如果是作用在不同的两个父子级上呢
我们文章开头,就是这样的一个例子
父级元素设置了transform: translateY(0)
然后他的子级上设置了一个fixed
,于是怪异的问题就发生了,fixed
失效了
页面结构大概就是这样
<divclass="wrap">
<divclass="subContent">我是fixed在最右侧</div>
<divclass="inner-content"></div>
</div>
对应的css
如下
.wrap{
height:300px;
border:1pxsolid#111;
margin:10px;
overflow-y:auto;
transform:translateY(0);
/*filter:blur(1px)*/
}
.subContent{
position:fixed;
right:10px;
top:20px;
background-color:red;
}
那为什么会出现这样的情况?我们画个图理解下
本质上transform:translate(0,0)
与(10px,-10px)
没有差别,图中这么画只是为了更好理解,因此我代码中设置的是translateY(0)
,所以其实是Y轴方向上往上偏移而已,但是这不影响我们理解这其中的本质。
因为外层父元素设置了transfrom
产生了堆叠上下文,而它子元素又想逃脱出去,儿子想造反给自己设置fixed
产生一个堆叠上下文,对不起,你必须听老子的,所以子元素设置的fixed
就失效了,你还是得跟着老子走
如果你不想因为父级元素transform
设置,你想单飞呢,你可以怎么做呢?
唯一的办法,另起炉灶….
因此你可以这么做
<divid="app">
<divclass="subContent">我是fixed在最右侧</div>
<divclass="wrap">
<divclass="inner-content"></div>
</div>
</div>
没错,你看到的就是,子级元素已经挣脱束缚了,所以我不受被包裹元素tranform
的影响了。
不知道你注意到没,其实filter
也是和transform
一样会产生堆叠上下文,如果子元素被包裹,父级元素设置filter
,那么子级元素的fixed
也会失效
是不是很惊讶,总之,一句话,父级如果产生了堆叠上下文,子级想要挣脱,对不起,必须听老子的。
好了,终于理清这个堆叠上下文的问题了,所以平时遇到那些奇怪的问题,试来试去,原来是一个css属性设置的原因造成的。
另外思考一个问题,当一个块级子级元素设置width:100%
与不设置width
,当我们对该元素设置margin
时,此时会发生什么?元素本身的宽度是怎么样的,这是一个我们经常遇到的一个问题,想清楚了,貌似你会对margin
的作用会有更深的认识。
总结
-
fixed失效的原因,主要是由于产生堆叠上下文造成的
-
理解堆叠上下文,什么条件会形成堆叠上下文
-
形成堆叠上下文主要由以下几种
- 文档流破坏:
float+margin
,定位postion
- css新特性:
transform
、filter
会产生堆叠上下文
- 文档流破坏:
-
同一个元素同时使用
poistion
与transform
哪个优先级更高权重更大,首先是会执行定位,然后再执行transform,因此定位的优先级更高,先执行,但是transform权重更大,会作用在定位之上 -
不同元素产生的堆叠上下文对子级元素造成的影响,如果一个父级产生堆叠上下文,那么它所有的子级元素都不会脱离父级,子元素设置的
fixed
会失效 -
最后安利张鑫旭老师的博文,文章很多思考来自
深入理解CSS中的层叠上下文和层叠顺序
这篇文章 -
本文示例源码code example[3]
参考资料
[1] issue: github.com/vuepress-re…
[2] 深入理解CSS中的层叠上下文和层叠顺序: www.zhangxinxu.com/wordpress/2…
[3] code example: ‘github.com/maicFir/les…