【问题标题】:Why does applying a CSS-Filter on the parent break the child positioning?为什么在父项上应用 CSS-Filter 会破坏子项定位?
【发布时间】:2022-11-18 05:06:51
【问题描述】:

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

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

接下来是完全相同的 sn-p,但增加了一个:我应用了一个过滤器,就我而言,它纯粹是装饰性的:filter: brightness(1.3);

正如您在下面看到的那样,当您在“动画”中滚动到一半时,标题就消失了。当您检查该元素时,它仍然具有所有属性,但不知何故消失了。这在 Firefox 和 Chrome 中是一样的,我不知道为什么。如果有人可以发布一个应用了过滤器的工作 sn-p 并解释为什么它以前不起作用,我将不胜感激。

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

【问题讨论】:

  • 如果这可以帮助任何人,html 标签的工作方式似乎与 chrome 中的其他标签不同,因此您可以对 chrome 中的 html 标签进行过滤并且它可以工作。我将其与手写笔一起使用,以实现通用的暗模式。
  • 无论好坏,Firefox 都正确地遵循规范(与 Chrome 不同),因此 Chrome 中的解决方法不是通用的,可能是暂时的。

标签: css css-position css-transforms css-filters


【解决方案1】:

如果我们参考the specification,我们可以阅读:

过滤器属性的值不是 none 会导致 创造一个绝对定位和固定定位的包含块 子孙除非它适用的元素是文档根 当前浏览上下文中的元素。功能列表是 按提供的顺序应用。

这意味着您的 position:fixed 元素将相对于过滤后的容器定位,而不再是视口。换句话说,它仍然是固定的,但在其内部新的包含块(过滤后的容器)

这是一个简化的版本来说明这个问题:

.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
  • transformref
  • backdrop-filterref
  • perspectiveref
  • containref
  • transform-styleref
  • content-visibilityref
  • will-change 与上述值之一一起使用时

如果属性的任何非初始值会导致元素为绝对定位元素生成包含块,则在 will-change 中指定该属性必须导致元素为绝对定位元素生成包含块。ref


1个: 我会尽量保持这个列表是最新的。

【讨论】:

  • 谢谢!我仍然想知道为什么这是一件事。如果不是这样的话不是更好吗?
  • @MiXT4PE 它是在规范中定义的,所以它应该是这样的,你可能会问他们为什么这样定义它......我很确定这个选择背后有一个原因(可能是一个复杂的原因)。
  • @ygoe 首先它不是错误,它是设计使然,就像我们不喜欢的许多功能一样,但我们应该使用(清除浮动、边距折叠等),所以你应该调整你的代码来克服这个问题。没有通用的解决方案,但根据您的情况,肯定有解决方案。您可以通过添加您的特定代码来提出问题,以查看我们是否可以修改您的逻辑以满足您的要求并解决问题。
  • 您的答案的更多上下文:this GitHub issue 添加了当 filter 的值不是 none 时创建包含块的规范。 GitHub 问题也有一个链接,指向决定该问题的讨论,this code example (archive link) 用于说明包含块旨在修复的歧义。
  • 规范问题402。 Firefox 错误 1650522
【解决方案2】:

如果你想模糊或灰化除一个元素之外的整个页面,只需使用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>

【讨论】:

  • 关于 Firefox 的一点更新:它似乎还没有推出,但可能很快就会推出。有关更新,请参阅bugzilla.mozilla.org/show_bug.cgi?id=1578503
  • 由于@Temani Afif backdrop-filter 与 filter 具有相同的效果
【解决方案3】:

为了避免每个浏览器的硬维护和不同实现,您可以使用递归树遍历函数来标记位置“固定”节点,然后在不破坏位置的情况下正确应用所需的过滤器。

我使用 this 代码来全局应用“filter invert”属性,而无需将其硬编码到特定的父元素。

【讨论】:

    【解决方案4】:

    您可以使用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

    【讨论】:

      猜你喜欢
      • 2019-03-27
      • 1970-01-01
      • 1970-01-01
      • 2010-09-06
      • 2018-02-08
      相关资源
      最近更新 更多