Reputation: 63657
I have a position: fixed;
navbar that I want to expand to the browser's height on click over 0.2 seconds.
You can see an example of the desired effect by clicking the "hamburger" on this page: http://iamwilliamstern.com/
Example / what I've tried:
Here is an example code pen where I try to do it: http://codepen.io/donpinkus/pen/qaodzP
I add the class .open
on click, which has the property bottom: 0
. Bottom is an animatable property, but the issue is I haven't set a value for it to animate from. Is my only option to use JS to calculate what the bottom
should be when it's "closed"?
I'm assuming there's a CSS only option to keep things smooth.
Upvotes: 2
Views: 2913
Reputation: 26771
What you can do is use the transform
CSS property to move your menu.
You will need to give this menu a 100%
height.
Then, in this demo, we will be using the :target
CSS pseudo-class to style the menu once the user clicks the anchor. You can use javascript to toggle a class if you want to, just change the selector.
.site__menu {
background: red;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
transition: transform .2s linear;
transform: translateY(85%);
}
.site__menu:target {
transform: translateY(0);
}
<section id="menu" class="site__menu">
<header class="site__header">
<nav>
<ul>
<li>
<a href="#menu">Click me to expand</a>
</li>
</ul>
</nav>
</header>
<section>
<h1>
Hello!
</h1>
<p>
Some content here.
</p>
</section>
</section>
Since the header
element's height can change, the transform
value will be hard to set.
That's why javascript will be used to get the header
height and set the translateY()
value.
The calculation is straight forward, using calc()
CSS function.
transform: translateY(calc(containerHeight - headerHeight));
Since we want to cover our whole container we use:
transform: translateY(calc(100% - headerHeight));
Demo Example:
transform: translateY(calc(100% - 76px));
var menu = $("#menu"),
menuTrigger = $("#menu-trigger"),
header = $(".site__header");
menuTrigger.on("click", function() {
menu.toggleClass("open");
});
function getHeaderHeight() {
var headerHeight = header.outerHeight(),
result = headerHeight + "px";
return result;
}
function transformMenu() {
var translateValue = getHeaderHeight();
menu.css("transform", "translateY(calc(100% - " + translateValue + "))");
}
transformMenu();
$(window).on("resize", function() {
transformMenu();
});
* {
box-sizing: border-box;
}
.site__menu {
background: dodgerblue;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
transition: transform .2s linear;
transform: translateY(100%);
}
/* Utility Class */
.site__menu.open {
transform: translateY(0) !important;
}
.site__nav ul {
list-style: none;
display: flex;
justify-content: space-between;
align-items: center;
margin: 0;
padding: 1em;
}
.menu__toggle {
cursor: pointer;
}
#spriteSheet {
display: none;
}
.icon {
display: inline-block;
width: 1em;
height: 1em;
stroke-width: 0;
stroke: currentColor;
fill: currentColor;
}
.icon-menu {
color: white;
font-size: 1.5em;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<section id="menu" class="site__menu">
<header class="site__header">
<nav class="site__nav">
<ul>
<li>
<img src="http://fillmurray.com/40/40" alt="Fill Murray Logo">
</li>
<li>
<a id="menu-trigger" class="menu__toggle">
<svg class="icon icon-menu">
<use xlink:href="#icon-menu"></use>
</svg>
</a>
</li>
</ul>
</nav>
</header>
<section>
<h1>
Hello!
</h1>
<p>
Some content here.
</p>
</section>
</section>
<svg id="spriteSheet">
<defs>
<symbol id="icon-menu" viewBox="0 0 32 32">
<title>menu</title>
<path class="path1" d="M2 6h28v6h-28zM2 14h28v6h-28zM2 22h28v6h-28z"></path>
</symbol>
</defs>
</svg>
Without jQuery library:
var menu = document.getElementById("menu"),
menuTrigger = document.getElementById("menu-trigger"),
header = document.querySelector(".site__header");
menuTrigger.addEventListener("click", function() {
menu.classList.toggle("open");
});
function getHeaderHeight() {
var headerHeight = header.offsetHeight + "px";
result = headerHeight;
return result;
}
function transformMenu() {
var translateValue = getHeaderHeight();
menu.style.transform = "translateY(calc(100% - " + translateValue + "))";
}
transformMenu();
window.addEventListener("resize", function() {
transformMenu();
});
* {
box-sizing: border-box;
}
.site__menu {
background: dodgerblue;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
transition: transform .2s linear;
transform: translateY(100%);
}
/* Utility Class */
.site__menu.open {
transform: translateY(0) !important;
}
.site__nav ul {
list-style: none;
display: flex;
justify-content: space-between;
align-items: center;
margin: 0;
padding: 1em;
}
.menu__toggle {
cursor: pointer;
}
#spriteSheet {
display: none;
}
.icon {
display: inline-block;
width: 1em;
height: 1em;
stroke-width: 0;
stroke: currentColor;
fill: currentColor;
}
.icon-menu {
color: white;
font-size: 1.5em;
}
<section id="menu" class="site__menu">
<header class="site__header">
<nav class="site__nav">
<ul>
<li>
<img src="http://fillmurray.com/40/40" alt="Fill Murray Logo">
</li>
<li>
<a id="menu-trigger" class="menu__toggle">
<svg class="icon icon-menu">
<use xlink:href="#icon-menu"></use>
</svg>
</a>
</li>
</ul>
</nav>
</header>
<section>
<h1>
Hello!
</h1>
<p>
Some content here.
</p>
</section>
</section>
<svg id="spriteSheet">
<defs>
<symbol id="icon-menu" viewBox="0 0 32 32">
<title>menu</title>
<path class="path1" d="M2 6h28v6h-28zM2 14h28v6h-28zM2 22h28v6h-28z"></path>
</symbol>
</defs>
</svg>
Upvotes: 0
Reputation: 48
here is mine solution: use min-height becouse css height is not "animatable"
http://codepen.io/KykooBG/pen/amYvJE
div {
background: red;
position: fixed;
top: 0;
left: 0;
right: 0;
transition: all .2s ease-in-out;
min-height: 15px;
}
.open {
min-height: 100vh;
}
Upvotes: -1
Reputation: 63657
Give the "closed" div a min-height: 1px
Give the "open" div a min-height: 100%
Since you are not manually setting height
, the "closed" nav will get its height from its content which is preferable to manually setting heights in CSS.
Example here: http://codepen.io/donpinkus/pen/amYvkL
div {
background: red;
position: fixed;
top: 0;
left: 0;
right: 0;
transition: all .2s ease-in-out;
min-height: 20px; // This gets animated
}
.open {
min-height: 100%;
}
Upvotes: 2
Reputation: 3149
Instead change bottom property add height for your initial div and change the same property in open class using 100 viewport height.
div {
background: red;
position: fixed;
top: 0;
left: 0;
right: 0;
transition: all .2s;
height: 80px;
}
.open {
height: 100vh;
}
Upvotes: 0