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>
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
transform-style
ref
content-visibility
ref
will-change
when used with one of the above valuesIf 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.
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With