Don P
Don P

Reputation: 63657

Transition a fixed position elements height

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

Answers (4)

Ricky Ruiz
Ricky Ruiz

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.


CODE SNIPPET:

.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>


EDIT:

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

Boyanov
Boyanov

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

Don P
Don P

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

xzegga
xzegga

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

Related Questions