skip to Main Content

I want to have HTML element to expand downwards and at the end I would like it to do a little bounce, something like cubic-bezier function could do.

So I have set transition: height ... in CSS to animate changes in height property of the element. However it works only when I have height set explicitly, and not as 100vh to just contain all elements.

To present it better, here are example: when the green container has max-height less then its actual height (to contain its children), then the effect is visible and working. But then I don’t rely of automatic sizing of the container to fit it’s children.

When I specify something "big enough" to accomodate all possible children, then of course, "bouncing effect" does not work, as it "bounces" when reaching the "big enough" height, making the effect not visible.

You can observe the issue by specifying different height in below snippet (3rem or below – works, 4 rem or above – does not work).

function expand(){
    let dropdown = document.getElementById('dropdown')
    let height = dropdown.offsetHeight
    let maxHeightInput = document.getElementById('maxHeightInput')
    let maxHeight = maxHeightInput?.value ?? 0
    dropdown.style.maxHeight = height > 0 ? 0 : maxHeight
}
#dropdown {
    background: lightgreen;
    max-height: 3rem;
    transition: max-height 0.4s cubic-bezier(.04,1.59,.83,1.18);
    & > ul {
        border: 1px solid black;    
    }
}
<label>Green container expanded max height <input id='maxHeightInput' value='3rem' /></label><br>
<p>List of items is ~3rems tall.</p>
<button onclick={expand()}>Expand / collapse</button>
<div id='dropdown'>
<ul>
<li>Item 1
<li>Item 2
<li>Item 3
</ul>
</div>

The question is how to make this bouncing effect work in my scenario: when I have container with children, and the container is sized dynamically based on its children.

2

Answers


  1. If I got your question right, and that’s to animate a parent element height with a bounce effect, here’s a viable solution (that uses a similar code as in this related answer: Animate height from 0 to auto) in order to create a bouncing effect for your collapsible parent.
    It uses delegated event, and a reusable component principle in that the target ID is stored inside the button’s data-* attribute:

    addEventListener("click", (evt) => {
      const elToggle = evt.target.closest("[data-toggle]");
      if (!elToggle) return;
      document.querySelector(elToggle.dataset.toggle).classList.toggle("expanded");
    });
    .dropdown {
      > * {
        padding: 0; margin: 0;
        list-style: none;
        background: lightgreen;
        
        overflow: hidden;
        interpolate-size: allow-keywords;
        transition: height 0.4s cubic-bezier(0.04, 1.59, 0.83, 1.18);
        block-size: 0; /* Or also:  height: 0; */
      }
    
      &.expanded > * {
        block-size: auto; /* Or also:  height: auto; */
      }
    }
    <button data-toggle="#dropdown-1" type="button">Expand / collapse</button>
    <div class="dropdown" id="dropdown-1">
      <ul>
        <li>Item 1</li>
        <li>Item 2</li>
        <li>Item 3</li>
      </ul>
    </div>
    
    <button data-toggle="#dropdown-2" type="button">Expand / collapse</button>
    <div class="dropdown" id="dropdown-2">
      <ul>
        <li>Item 1</li>
        <li>Item 2</li>
        <li>Item 3</li>
        <li>Item 4</li>
        <li>Item 5</li>
        <li>Item 6</li>
      </ul>
    </div>
    Login or Signup to reply.
  2. Since you’re using JS anyway, you might transition height toggling it between 0 and scrollHeight:

    function expand() {
      const dropdown = document.getElementById('dropdown');
      dropdown.style.height = dropdown.offsetHeight > 2 ? 0 : dropdown.scrollHeight + 'px'
    }
    #dropdown {
      background: lightgreen;
      height: 0;
      overflow: hidden;
      transition: height 0.4s cubic-bezier(.04, 1.59, .83, 1.18);
      border: 1px solid black;
    }
    <button onclick="expand()">Expand / collapse</button>
    <div id='dropdown'>
      <ul>
        <li>Item 1
        <li>Item 2
        <li>Item 3
      </ul>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search