为什么在 parent 上应用 CSS-Filter 会破坏 child 定位?
Why does applying a CSS-Filter on the parent break the child positioning?
所以我有这个 title-screen“动画”,它的标题在全屏页面上居中,当您向下滚动时,它会变小并保持在页面顶部。这是一个具有预期行为的工作示例,我从中删除了所有不必要的代码以使其最小化:
$(window).scroll( () => {
"use strict";
let windowH = $(window).height();
let windowS = $(window).scrollTop();
let header = $("#header").height();
if (windowS < windowH-header) {
$("#title").css("transform", "scale("+(2-(windowS/($(document).outerHeight()-windowH))*2.7)+")");
$("#header").css("transform", "translateY(0)");
$("#inside, #content").css({
"position": "static",
"margin-top": 0
});
} else {
$("#inside").css({
"position": "fixed",
"margin-top": -windowH+header
});
$("#content").css("margin-top", windowH);
}
$("#header").css("position", windowS > (windowH-header)/2 ? "fixed" :"static");
});
.fixed {
position: fixed!important;
}
.wrapper {
width: 100%;
text-align: center;
}
.wrapper:before {
display: table;
content: " ";
}
.wrapper:after {
clear: both;
}
#inside {
width: 100%;
height: 100vh;
background-color: lightcoral;
display: flex;
align-items: center;
justify-content: center;
}
#header {
height: 90px;
top: 0;
position: sticky;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.5s;
}
#title {
width: 100%;
color: #fff;
transform: scale(2);
}
#content {
height: 1000px;
background-color: lightblue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div class="wrapper">
<div id="inside">
<div id="header">
<h1 id="title">Title</h1>
</div>
</div>
<div id="content"></div>
</body>
接下来是完全相同的片段,但增加了一个:我应用了一个过滤器,就我而言,它纯粹是装饰性的:filter: brightness(1.3);
。
正如您在下面看到的那样,当您滚动 half-way 浏览“动画”时,标题就消失了。当您检查该元素时,它仍然具有所有属性,但不知何故消失了。这在 Firefox 和 Chrome 中是一样的,我不知道为什么。如果有人可以 post 应用过滤器的工作片段并解释为什么它以前不起作用,我将不胜感激。
$(window).scroll( () => {
"use strict";
let windowH = $(window).height();
let windowS = $(window).scrollTop();
let header = $("#header").height();
if (windowS < windowH-header) {
$("#title").css("transform", "scale("+(2-(windowS/($(document).outerHeight()-windowH))*2.7)+")");
$("#header").css("transform", "translateY(0)");
$("#inside, #content").css({
"position": "static",
"margin-top": 0
});
} else {
$("#inside").css({
"position": "fixed",
"margin-top": -windowH+header
});
$("#content").css("margin-top", windowH);
}
$("#header").css("position", windowS > (windowH-header)/2 ? "fixed" :"static");
});
.fixed {
position: fixed!important;
}
.wrapper {
width: 100%;
text-align: center;
}
.wrapper:before {
display: table;
content: " ";
}
.wrapper:after {
clear: both;
}
#inside {
width: 100%;
height: 100vh;
background-color: lightcoral;
filter: brightness(1.3); /*<<<<<<<<<<<<<<<<*/
display: flex;
align-items: center;
justify-content: center;
}
#header {
height: 90px;
top: 0;
position: sticky;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.5s;
}
#title {
width: 100%;
color: #fff;
transform: scale(2);
}
#content {
height: 1000px;
background-color: lightblue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div class="wrapper">
<div id="inside">
<div id="header">
<h1 id="title">Title</h1>
</div>
</div>
<div id="content"></div>
</body>
如果我们参考the specification我们可以读到:
A value other than none for the filter property results in the
creation of a containing block for absolute and fixed positioned
descendants unless the element it applies to is a document root
element in the current browsing context. The list of functions are
applied in the order provided.
这意味着您的 position:fixed
元素将相对于过滤后的容器定位,而不再是视口。换句话说,它仍然是固定的,但在其 new 包含块(过滤容器)
中
这里有一个简化版本来说明这个问题:
.container {
display: inline-block;
width: 200px;
height: 200vh;
border: 1px solid;
}
.container>div {
position: fixed;
width: 100px;
height: 100px;
background: red;
color: #fff;
}
<div class="container">
<div>I am fixed on scroll</div>
</div>
<div class="container" style="filter:grayscale(1);">
<div>I move with the scroll</div>
</div>
要解决此问题,请尝试将过滤器移至固定元素而不是其容器:
.container {
display: inline-block;
width: 200px;
height: 200vh;
border: 1px solid;
}
.container>div {
position: fixed;
width: 100px;
height: 100px;
background: red;
color: #fff;
filter: grayscale(1);
}
<div class="container">
<div>I am fixed on scroll</div>
</div>
这是一个非详尽无遗的1属性列表,导致为绝对和固定定位后代创建包含块
filter
transform
ref
backdrop-filter
ref
perspective
ref
contain
ref
transform-style
ref
content-visibility
ref
will-change
与上述值之一一起使用时
If any non-initial value of a property would cause the element to generate a containing block for absolutely positioned elements, specifying that property in will-change must cause the element to generate a containing block for absolutely positioned elements. ref
1:我会尽量更新这个列表。
为了避免硬维护和每个浏览器的不同实现,您可以使用标记位置 'fixed' 节点的递归树遍历函数,然后在不破坏位置的情况下正确应用所需的过滤器。
我使用 this 代码来全局应用 'filter invert' 属性,而不是将其硬编码到特定的父元素。
如果要对除一个元素以外的整个页面进行模糊处理或灰度化,只需使用 backdrop-filter
而不是 filter
。
在撰写本文时,Firefox 默认只需要 ship。
不工作:
<!DOCTYPE html><html><head>
<style>
.overlay {
position:fixed;
right:10%;
top:10%;
bottom:10%;
left:10%;
background-color:red;
z-index:2;
}
.overlay ~ * {
filter: grayscale(50%) blur(5px);
}
</style>
</head>
<body>
<div class="overlay">I am not blurred</div>
<!-- position test -->
<div style="margin-top:100px;float:right;">
<div style="background-color:red;position:fixed;top:0;right:0;width:100px;height:100px;"></div>
</div>
</body>
</html>
工作:
<!DOCTYPE html><html><head>
<style>
.overlay {
position:fixed;
right:10%;
top:10%;
bottom:10%;
left:10%;
background-color:red;
z-index:2;
}
body:after {
content:"";
position:fixed;
z-index: 1;
top:0;
left:0;
width:100%;
height:100%;
backdrop-filter: grayscale(50%) blur(5px);
}
</style>
</head>
<body>
<div class="overlay">I am not blurred</div>
<!-- position test -->
<div style="margin-top:100px;float:right;">
<div style="background-color:red;position:fixed;top:0;right:0;width:100px;height:100px;"></div>
</div>
</body>
</html>
您可以使用 before
来实现它。而不是直接将 filter: brightness(1.3);
分配给子元素。将颜色和过滤器分配给此子元素的 before
。所以你的代码应该是这样的:
#inside:before{
filter: brightness(1.3);
content: "";
background-color: lightcoral;
top: 0;
left: 0;
width: 100%;
height: 100%;
position: absolute;
}
这将解决问题。
您还应该编辑一些代码以使其完全正常工作:
#inside{
position: relative;
}
将父位置设为相对位置,以确保 before
包裹在其父位置内。
此解决方案也适用于其他过滤器,例如 backdrop-filter
。
所以我有这个 title-screen“动画”,它的标题在全屏页面上居中,当您向下滚动时,它会变小并保持在页面顶部。这是一个具有预期行为的工作示例,我从中删除了所有不必要的代码以使其最小化:
$(window).scroll( () => {
"use strict";
let windowH = $(window).height();
let windowS = $(window).scrollTop();
let header = $("#header").height();
if (windowS < windowH-header) {
$("#title").css("transform", "scale("+(2-(windowS/($(document).outerHeight()-windowH))*2.7)+")");
$("#header").css("transform", "translateY(0)");
$("#inside, #content").css({
"position": "static",
"margin-top": 0
});
} else {
$("#inside").css({
"position": "fixed",
"margin-top": -windowH+header
});
$("#content").css("margin-top", windowH);
}
$("#header").css("position", windowS > (windowH-header)/2 ? "fixed" :"static");
});
.fixed {
position: fixed!important;
}
.wrapper {
width: 100%;
text-align: center;
}
.wrapper:before {
display: table;
content: " ";
}
.wrapper:after {
clear: both;
}
#inside {
width: 100%;
height: 100vh;
background-color: lightcoral;
display: flex;
align-items: center;
justify-content: center;
}
#header {
height: 90px;
top: 0;
position: sticky;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.5s;
}
#title {
width: 100%;
color: #fff;
transform: scale(2);
}
#content {
height: 1000px;
background-color: lightblue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div class="wrapper">
<div id="inside">
<div id="header">
<h1 id="title">Title</h1>
</div>
</div>
<div id="content"></div>
</body>
接下来是完全相同的片段,但增加了一个:我应用了一个过滤器,就我而言,它纯粹是装饰性的:filter: brightness(1.3);
。
正如您在下面看到的那样,当您滚动 half-way 浏览“动画”时,标题就消失了。当您检查该元素时,它仍然具有所有属性,但不知何故消失了。这在 Firefox 和 Chrome 中是一样的,我不知道为什么。如果有人可以 post 应用过滤器的工作片段并解释为什么它以前不起作用,我将不胜感激。
$(window).scroll( () => {
"use strict";
let windowH = $(window).height();
let windowS = $(window).scrollTop();
let header = $("#header").height();
if (windowS < windowH-header) {
$("#title").css("transform", "scale("+(2-(windowS/($(document).outerHeight()-windowH))*2.7)+")");
$("#header").css("transform", "translateY(0)");
$("#inside, #content").css({
"position": "static",
"margin-top": 0
});
} else {
$("#inside").css({
"position": "fixed",
"margin-top": -windowH+header
});
$("#content").css("margin-top", windowH);
}
$("#header").css("position", windowS > (windowH-header)/2 ? "fixed" :"static");
});
.fixed {
position: fixed!important;
}
.wrapper {
width: 100%;
text-align: center;
}
.wrapper:before {
display: table;
content: " ";
}
.wrapper:after {
clear: both;
}
#inside {
width: 100%;
height: 100vh;
background-color: lightcoral;
filter: brightness(1.3); /*<<<<<<<<<<<<<<<<*/
display: flex;
align-items: center;
justify-content: center;
}
#header {
height: 90px;
top: 0;
position: sticky;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.5s;
}
#title {
width: 100%;
color: #fff;
transform: scale(2);
}
#content {
height: 1000px;
background-color: lightblue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div class="wrapper">
<div id="inside">
<div id="header">
<h1 id="title">Title</h1>
</div>
</div>
<div id="content"></div>
</body>
如果我们参考the specification我们可以读到:
A value other than none for the filter property results in the creation of a containing block for absolute and fixed positioned descendants unless the element it applies to is a document root element in the current browsing context. The list of functions are applied in the order provided.
这意味着您的 position:fixed
元素将相对于过滤后的容器定位,而不再是视口。换句话说,它仍然是固定的,但在其 new 包含块(过滤容器)
这里有一个简化版本来说明这个问题:
.container {
display: inline-block;
width: 200px;
height: 200vh;
border: 1px solid;
}
.container>div {
position: fixed;
width: 100px;
height: 100px;
background: red;
color: #fff;
}
<div class="container">
<div>I am fixed on scroll</div>
</div>
<div class="container" style="filter:grayscale(1);">
<div>I move with the scroll</div>
</div>
要解决此问题,请尝试将过滤器移至固定元素而不是其容器:
.container {
display: inline-block;
width: 200px;
height: 200vh;
border: 1px solid;
}
.container>div {
position: fixed;
width: 100px;
height: 100px;
background: red;
color: #fff;
filter: grayscale(1);
}
<div class="container">
<div>I am fixed on scroll</div>
</div>
这是一个非详尽无遗的1属性列表,导致为绝对和固定定位后代创建包含块
filter
transform
refbackdrop-filter
refperspective
refcontain
reftransform-style
refcontent-visibility
refwill-change
与上述值之一一起使用时
If any non-initial value of a property would cause the element to generate a containing block for absolutely positioned elements, specifying that property in will-change must cause the element to generate a containing block for absolutely positioned elements. ref
1:我会尽量更新这个列表。
为了避免硬维护和每个浏览器的不同实现,您可以使用标记位置 'fixed' 节点的递归树遍历函数,然后在不破坏位置的情况下正确应用所需的过滤器。
我使用 this 代码来全局应用 'filter invert' 属性,而不是将其硬编码到特定的父元素。
如果要对除一个元素以外的整个页面进行模糊处理或灰度化,只需使用 backdrop-filter
而不是 filter
。
在撰写本文时,Firefox 默认只需要 ship。
不工作:
<!DOCTYPE html><html><head>
<style>
.overlay {
position:fixed;
right:10%;
top:10%;
bottom:10%;
left:10%;
background-color:red;
z-index:2;
}
.overlay ~ * {
filter: grayscale(50%) blur(5px);
}
</style>
</head>
<body>
<div class="overlay">I am not blurred</div>
<!-- position test -->
<div style="margin-top:100px;float:right;">
<div style="background-color:red;position:fixed;top:0;right:0;width:100px;height:100px;"></div>
</div>
</body>
</html>
工作:
<!DOCTYPE html><html><head>
<style>
.overlay {
position:fixed;
right:10%;
top:10%;
bottom:10%;
left:10%;
background-color:red;
z-index:2;
}
body:after {
content:"";
position:fixed;
z-index: 1;
top:0;
left:0;
width:100%;
height:100%;
backdrop-filter: grayscale(50%) blur(5px);
}
</style>
</head>
<body>
<div class="overlay">I am not blurred</div>
<!-- position test -->
<div style="margin-top:100px;float:right;">
<div style="background-color:red;position:fixed;top:0;right:0;width:100px;height:100px;"></div>
</div>
</body>
</html>
您可以使用 before
来实现它。而不是直接将 filter: brightness(1.3);
分配给子元素。将颜色和过滤器分配给此子元素的 before
。所以你的代码应该是这样的:
#inside:before{
filter: brightness(1.3);
content: "";
background-color: lightcoral;
top: 0;
left: 0;
width: 100%;
height: 100%;
position: absolute;
}
这将解决问题。
您还应该编辑一些代码以使其完全正常工作:
#inside{
position: relative;
}
将父位置设为相对位置,以确保 before
包裹在其父位置内。
此解决方案也适用于其他过滤器,例如 backdrop-filter
。