Flex display acts differently in Chrome and Safari
Background:
In a tool that my team is working on, we are implementing header
which is programmatically output from the screen to an event listener. After writing the first implementation, it worked as expected, but only in Chrome.
I use display:flex
for #wrapper
as well as for items #content
.
<div id="wrapper">
<header>Header Content</header>
...
<section id="content">
<div id="subcontent">
<div id="tool">
<div id="tool-container"></div>
<svg id="svgTool" height="100%" width="100%" class="svgTool style-scope test-tool"></svg>
</div>
</div>
</section>
...
</div>
some of the css:
...
#wrapper {
width:100%;
height:100vh;
position:relative;
display:flex;
flex-direction:column;
margin-left:auto;
margin-right:auto;
min-height:100%;
}
#content {
display: flex;
flex-direction: column;
height: 100%;
}
...
Using some simple JavaScript, I customize the margin-top
element header
on click of a button (for this example).
Problem: While Chrome works as expected, Safari does not. I suspect it must have something to do with how the Safari website version interprets the elements with display: flex
. edit: or maybe it has something to do with how Safari works with vh
units?
In Chrome, the title will move up and down without affecting the element <svg>
. In Safari, adjusting the margin-top
title will cause the element to svg
create a space at the bottom of the screen equal to the change in header
margin-top
.
JSBin: This JSbin works identical to the current implementation of our tool. Open it in both Chrome and Safari to see the discrepancy.
http://jsbin.com/kaxiqig/edit?html,css,output
sidenote: firefox also works as expected
source to share
Instead of a real fix for what appears to be a bug, a simple workaround is to switch the height main
while you also switch the position of the header. Basically, it seems like Safari pulls main
up with the title when it goes up, so we'll just make it taller so that it expands again to the bottom of the screen.
The default height main
is calc(100vh - 80px)
, so we can just change the other half of the calc expression to get our new value. Exactly how you change it depends on how tall the header is in your application. Let's say that in this case, the title is usually 100px high. Since the title is translated as margin-top: -75px
, we can simply change our calc expression to calc(100vh - 25px)
for the new height.
While I originally suggested switching minimum-height
to main
, I noticed that we also get an ugly error when doing this where the minimum height change is not quite correct, even if we add a suitable transition property, leaving a white gap at the bottom of the screen for a second. This way we can just animate the actual one height
, which gives a nice smooth result.
In your CSS, I added this line:
main {
transition: height .5s;
}
The new slide function becomes:
function slide() {
if (document.querySelector('header').style.marginTop !== "-75px") {
document.querySelector('header').style.transition = ".5s";
document.querySelector('header').style.marginTop = "-75px";
document.querySelector('main').style.height = "calc(100vh - 25px)";
} else {
document.querySelector('header').style.marginTop = "0";
document.querySelector('main').style.height = "";
}
}
If your title will change in height, you can instead switch the class to main
and revise the calc expression that uses the class with different media queries, or you can improve the function to find the height of the viewport first and then subtract the height of the post-transition title to find new height to apply to main
.
source to share