Why does applying a CSS-Filter on the parent break the child positioning?

前端 未结 3 1341
后悔当初
后悔当初 2020-11-22 03:28

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

相关标签:
3条回答
  • 2020-11-22 03:51

    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.

    0 讨论(0)
  • 2020-11-22 04:01

    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 filtred container and no more the viewport. In other words, it's still fixed but inside its new containing block (the filtred 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
    • transform-style 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: Will try to keep this list up to date.

    0 讨论(0)
  • 2020-11-22 04:11

    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>

    0 讨论(0)
提交回复
热议问题