March 30, 2023
Slide up/down animation with AlpineJS
With CSS it is not possible to transition the height property to auto:
1.collapsible {2 height: 0;3 overflow: hidden;4 transition: height .3s;5}6 7.collapsible.is-open {8 height: auto;9}
For years developers relied on jQuery's (or Velocity's) slideUp() and slideDown() methods to perform slide up/down animations without setting a fixed height. In fact, I remember being brought into projects where jQuery was added to the project just for these 2 methods.
Here is how to implement slide up/down animation with Alpine JS.
Component set up
Start with an Alpine component with a property to manage the open state of a collapsible element. And let's add a .collapsible element.
1<div x-data="{isOpen: false}">2 <div class="collapsible">3 <!-- your content -->4 </div>5</div>
CSS
Then we add the CSS styles for the collapsed state:
1.collapsible {2 height: 0;3 opacity: 0;4 overflow: hidden;5 transition: all .4s;6 pointer-events: none;7}
And then we add the CSS for the expanded state (excluding the height which we'll manage with Alpine):
1.collapsible.is-open {2 opacity: 1;3 pointer-events: auto;4}
The magic
What we want to do when the isOpen property is set to true:
- Add the
.is-openclass to the.collapsibleelement - Programmatically set a
heightto the.collapsibleelement
For (1), we'll use x-bind to conditionally set a class based on the value of the isOpen property. This can be written as x-bind:class or :class:
1<div x-data="{isOpen: false}">2 <div3 class="collapsible"4 :class="isOpen && 'is-open'"5 >6 <!-- your content -->7 </div>8</div>
For (2), we'll also use x-bind, but to add some inline styles. We'll add the height property and give it a value we calculate with JS.
"Calculate" is a misleading term here. We are going to literally read the height of the element using scrollHeight which includes the height of an element's visible and invisible content. We'll access the element's DOM node using Alpine's $el magic property.
1<div x-data="{isOpen: false}">2 <div3 class="collapsible"4 :class="isOpen && 'is-open'"5 :style="isOpen && {height: $el.scrollHeight+`px`}"6 >7 <!-- your content -->8 </div>9</div>
Here is the full demo including a button to expand/collapse the element: