【问题标题】:Why does applying a CSS-Filter on the parent break the child positioning?为什么在父级上应用 CSS-Filter 会破坏子级定位?
【发布时间】:2019-03-27 00:31:59
【问题描述】:

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

$(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 会导致 为绝对定位和固定定位创建一个包含块 descendants 除非它适用的元素是文档根 当前浏览上下文中的元素。函数列表是 按提供的顺序应用。

这意味着您的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
  • 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>

【讨论】:

【解决方案3】:

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

我使用this 代码来全局应用“过滤反转”属性,而不是将其硬编码到特定的父元素。

【讨论】:

    猜你喜欢
    • 2022-11-18
    • 2021-12-31
    • 2014-05-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多