为什么在父母中应用CSS滤波器打破孩子的定位?

发布于 2025-02-08 11:23:39 字数 4477 浏览 1 评论 0 原文

因此,我有这个标题屏幕“动画”,标题以全屏页面为中心,当您向下滚动时,它会变小并保持在页面顶部。这是一个具有预期行为的工作示例,我从中剥离了所有不必要的代码,以使其最少:

$(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>

接下来是完全相同的片段,但有一个添加:我应用了一个过滤器,就我而言,它纯粹是化妆品:滤镜:亮度:亮度(1.3);

如下所示,当您滚动到“动画”中途滚动时,标题就消失了。当您检查元素时,它仍然具有其所有属性,但它以某种方式消失了。在Firefox和Chrome中也是如此,我不知道为什么。如果有人可以发布使用过滤器的工作片段,并解释为什么它以前不起作用,我将非常感谢它。

$(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>

So I have this title-screen "animation" that has the title centered on a fullscreen page and when you scroll down it becomes smaller and remains at the top of the page. Here is a working example with the expected behavior, from which I stripped all unnecessary code to make it minimal:

$(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>

Next up is the exact same snippet but with one addition: I applied a filter, which is, as far as I'm concerned, purely cosmetic: filter: brightness(1.3);.

As you can see below when you scroll half-way through the "animation" the title just disappears. When you inspect the element it still has all its properties but somehow it's gone. This is the same in Firefox and Chrome and I have no idea why. I would appreciate it a lot if someone could post a working snippet with the filter applied and explain why it didn't work before.

$(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>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

你丑哭了我 2025-02-15 11:23:39

如果我们参考规范我们可以阅读:

对于滤波器属性而不是没有值的值
创建包含绝对定位和固定位置的块
后代除非它适用的元素是文档根
当前浏览上下文中的元素。功能列表是
按照提供的顺序应用。

这意味着您的位置:固定元素将相对放置在过滤后的容器上,而不再是视口。换句话说,它仍然是固定的,但在其 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 导致创建绝对和固定位置后代的包含块的属性列表

如果属性的任何非初始值会导致该元素为绝对定位的元素生成一个包含块,并指定遗嘱中的属性必须导致该元素为绝对位置的元素生成一个包含块。 ref ref

1 :我将尝试将此列表保持最新。

If we refer to the specification we can read:

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.

This means that your position:fixed element will be positioned relatively to the filtered container and no more the viewport. In other words, it's still fixed but inside its new containing block (the filtered container)

Here is a simplified version to illustrate the issue:

.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>

To fix the issue try to move the filter to the fixed element instead of its container:

.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>


Here is a non-exhaustive1 list of the properties that results in the creation of a containing block for absolute and fixed positioned descendants

  • filter
  • transform ref
  • backdrop-filter ref
  • perspective ref
  • contain ref (when used with the layout value)
  • container ref (It's no more the case)
  • transform-style ref
  • content-visibility ref
  • will-change when used with one of the above values

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: I will try to keep this list up to date.

奶茶白久 2025-02-15 11:23:39

您可以在之前使用实现它。而不是将过滤器分配给亮度(1.3); 直接直接为子元素。在此子元素的之前,将颜色和过滤器分配给。因此,您的代码应该是这样的:

  #inside:before{
    filter: brightness(1.3);
    content: "";
    background-color: lightcoral;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    position: absolute;
}

这将解决问题。

您还应该编辑一些代码以使其完全工作:

#inside{
    position: relative;
}

使父位置相对确保包装在其父母内部。

该解决方案还可以与其他过滤器(例如Backdrop-Filter )一起使用。

You can make it happen by using before. instead of assigning the filter: brightness(1.3); to the child element directly. assign color and filter to the before of this child element. so your code should be like this:

  #inside:before{
    filter: brightness(1.3);
    content: "";
    background-color: lightcoral;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    position: absolute;
}

This will fix the problem.

You should also edit some of your code to make this fully work:

#inside{
    position: relative;
}

Make the parent position to be relative to make sure the before wrap inside its parent.

This solution also works with other filters like backdrop-filter.

梦幻的心爱 2025-02-15 11:23:39

为了避免对每个浏览器进行硬维护和不同的实现,您可以使用递归树遍历功能,该功能标记“固定”节点的位置,然后正确应用所需的过滤器而不会破坏位置。

我使用 this 代码以便在全球范围内应用'filter' - 将其编码为特定的父元素。

In order to avoid hard maintenance and different implementation for each browser, you can use a recursive tree traversal function which marks the position 'fixed' nodes and then apply the needed filter properly without destroying the positions.

I used this code in order to apply the 'filter invert' property globally, without hard-coding it to specific parent elements.

你在看孤独的风景 2025-02-15 11:23:39

如果要模糊或灰度整个页面,除一个元素外,只需使用 Backdrop-Filter 而不是 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>

If you want to blur or grayscale the entire page except one element, just use backdrop-filter instead of filter.
At the time of writing Firefox just needs to ship it by default.

Not working:

<!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>

Working:

<!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>

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文