I want to implement a vanilla JS version of the jQuery slideUp()
and slideDown()
function.
My idea was to use CSS transition
for the height
property together with incrementing/decrementing the height using requestAnimationFrame()
.
I tried it with the following code, and slideDown
is working as expected, but slideUp
is not smoothly collapsing, instead, it is just suddenly gone.
Here is how it looks:
Here is a jsfiddle.
function toggleSlide(element) {
// Check if the element is currently visible
if (element.style.display== '' || element.style.display== 'none') {
// If the element is not visible, slide it down
slideDown(element);
} else {
// If the element is visible, slide it up
slideUp(element);
}
}
function slideDown(element) {
// If the element is not visible, set the display and height properties
if (element.style.display !== 'block') {
element.style.display = 'block';
element.style.height = 0;
}
// Get the height of the element's content
const contentHeight = element.scrollHeight;
// Set a variable to keep track of the element's height
let currentHeight = 0;
// Start an animation loop
function animate() {
// Increase the element's height by 10px
currentHeight = 10;
// Update the element's height
element.style.height = `${currentHeight}px`;
// If the element's height is less than the content height, request another animation frame
if (currentHeight < contentHeight) {
requestAnimationFrame(animate);
}
}
// Start the animation
animate();
}
function slideUp(element) {
// Get the height of the element's content
const contentHeight = element.scrollHeight;
// Set a variable to keep track of the element's height
let currentHeight = contentHeight;
// Start an animation loop
function animate() {
// Decrease the element's height by 10px
currentHeight -= 10;
// Update the element's height
element.style.height = `${currentHeight}px`;
// If the element's height is greater than 0, request another animation frame
if (currentHeight > 0) {
requestAnimationFrame(animate);
} else {
// If the animation is complete, hide the element
element.style.display = 'none';
}
}
// Start the animation
animate();
}
.my-element {
background-color: #ddd;
transition: height 0.5s;
overflow: hidden;
display:none;
}
<button onclick="toggleSlide(document.querySelector('.my-element'))">Slide Down</button>
<!-- Create the element that we will slide down -->
<div >
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. In et tellus in quam convallis dictum. Maecenas eget odio eget arcu accumsan porttitor. Ut elementum volutpat orci, sed tincidunt ipsum dictum at. Maecenas auctor tempus diam quis gravida. Aliquam at ultricies leo. Etiam euismod, nisi ac blandit placerat, diam turpis vestibulum diam, at ultricies turpis orci at elit. Mauris auctor dictum dolor, quis pretium nisi ultricies in. Aenean sollicitudin, quam non euismod porta, enim nisl laoreet velit, vel venenatis dui dui vel lacus. Aliquam erat volutpat. Donec et elit ut ipsum elementum volutpat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent a nisi at est eleifend ullamcorper. Aenean porta, erat ac sagittis fermentum, leo magna tincidunt lectus, ac semper risus est et diam. Suspendisse auctor ipsum quam, id porta dui dictum sed. Vivamus ac diam non elit placerat iaculis vel at velit. Aliquam vel tincidunt velit. Duis imperdiet, dolor eu placerat luctus, ipsum quam laoreet dui, non suscipit nulla tellus et arcu. Donec dictum, massa non bibendum varius, leo urna condimentum diam, nec suscipit elit turpis vel turpis. Aenean ac nunc quis nisl tempus tincidunt a eu metus.</p>
</div>
Why is slideUp
not collapsing smoothly?
CodePudding user response:
The CSS Transition is the one taking effect before the toggle animation ends. Removing it solves the issue since your functions are taking care of the animating durations
function toggleSlide(element) {
// Check if the element is currently visible
if (element.style.display== '' || element.style.display== 'none') {
// If the element is not visible, slide it down
slideDown(element);
} else {
// If the element is visible, slide it up
slideUp(element);
}
}
function slideDown(element) {
// If the element is not visible, set the display and height properties
if (element.style.display !== 'block') {
element.style.display = 'block';
element.style.height = 0;
}
// Get the height of the element's content
const contentHeight = element.scrollHeight;
// Set a variable to keep track of the element's height
let currentHeight = 0;
// Start an animation loop
function animate() {
// Increase the element's height by 10px
currentHeight = 10;
// Update the element's height
element.style.height = `${currentHeight}px`;
// If the element's height is less than the content height, request another animation frame
if (currentHeight < contentHeight) {
requestAnimationFrame(animate);
}
}
// Start the animation
animate();
}
function slideUp(element) {
// Get the height of the element's content
const contentHeight = element.scrollHeight;
// Set a variable to keep track of the element's height
let currentHeight = contentHeight;
// Start an animation loop
function animate() {
// Decrease the element's height by 10px
currentHeight -= 10;
// Update the element's height
element.style.height = `${currentHeight}px`;
// If the element's height is greater than 0, request another animation frame
if (currentHeight > 0) {
requestAnimationFrame(animate);
} else {
// If the animation is complete, hide the element
element.style.display = 'none';
}
}
// Start the animation
animate();
}
.my-element {
background-color: #ddd;
/* Remove the transition */
overflow: hidden;
display:none;
}
<button onclick="toggleSlide(document.querySelector('.my-element'))">Slide Down</button>
<!-- Create the element that we will slide down -->
<div >
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. In et tellus in quam convallis dictum. Maecenas eget odio eget arcu accumsan porttitor. Ut elementum volutpat orci, sed tincidunt ipsum dictum at. Maecenas auctor tempus diam quis gravida. Aliquam at ultricies leo. Etiam euismod, nisi ac blandit placerat, diam turpis vestibulum diam, at ultricies turpis orci at elit. Mauris auctor dictum dolor, quis pretium nisi ultricies in. Aenean sollicitudin, quam non euismod porta, enim nisl laoreet velit, vel venenatis dui dui vel lacus. Aliquam erat volutpat. Donec et elit ut ipsum elementum volutpat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Praesent a nisi at est eleifend ullamcorper. Aenean porta, erat ac sagittis fermentum, leo magna tincidunt lectus, ac semper risus est et diam. Suspendisse auctor ipsum quam, id porta dui dictum sed. Vivamus ac diam non elit placerat iaculis vel at velit. Aliquam vel tincidunt velit. Duis imperdiet, dolor eu placerat luctus, ipsum quam laoreet dui, non suscipit nulla tellus et arcu. Donec dictum, massa non bibendum varius, leo urna condimentum diam, nec suscipit elit turpis vel turpis. Aenean ac nunc quis nisl tempus tincidunt a eu metus.</p>
</div>
CodePudding user response:
As Barmar stated, one should either use requestAnimationFrame()
or CSS transitions. Cypherjac represents the solution of just using requestAnimationFrame()
here comes the alternative CSS transitions solution:
See jsfiddle or
const button = document.querySelector('button')
button.addEventListener('click', (e) => {
const container = document.querySelector('div');
const active = container.style.height !== '' && container.style.height !== '0px';
if(active){
container.style.height = "0px"
container.addEventListener('transitionend', () => {
container.style.display = 'none';
}, {once: true})
return;
}
container.style.display = 'block';
container.style.height = "auto"
const height = container.clientHeight "px"
container.style.height = "0px"
setTimeout(() => {
container.style.height = height
}, 0)
})
.box {
transition: height .5s ease;
overflow : hidden;
height:0;
}
<div >
I'm an unknown content height element. I'm an unknown content height element. I'm an unknown content height element. I'm an unknown content height element. I'm an unknown content height element. I'm an unknown content height element. I'm an unknown content height element. I'm an unknown content height element. I'm an unknown content height element. I'm an unknown content height element. I'm an unknown content height element. I'm an unknown content height element. I'm an unknown content height element. I'm an unknown content height element. I'm an unknown content height element.
</div>
<button>Slide Toggle</button>
The part with setting display
to none
/block
could be omitted. Only added, as I assume for most scenarios, its useful to have it completly removed from flow when toggled.
The addEventListener:transitioned
is necessary, so the display is set after the height has been changed to 0 and CSS transition has taken place.
The setTimeout
is necessary, because otherwise, the height would be directly set to height
and CSS would not get the change to animate.