I find it absurd to use jQuery for a couple of effects like slideToggle. Just because of that, I wrote this handy vanilla JavaScript slideToggle function recently.

This works exactly the same as jQuery slideToggle. Have a look at its working demonstration here.

JavaScript SlideToggle
Live Demo

The jQuery library comes with a plenty of tempting features which also make it too heavy in file size i.e approx 85KB. It may also take a lot to do the same effects with vanilla JavaScript. Let’s see how complicated it is to pull off a function like slideToggle with plain JavaScript.

JavaScript SlideToggle Concept

Before doing anything, I looked up the web and found some interesting solutions. But none was as smooth as jQuery’s slideUp, slideDown, and slideToggle.

Of course you gotta add and remove the dimensional and transition CSS properties for the target with JavaScript. But mind you, we don’t actually need much of JavaScript for the animation logic. We are going to add and remove a specific set of CSS properties at the right time to emulate sliding animation.

Below is the summary of what we are going to do ahead of here:

  1. Create separate functions for sliding-up and sliding-down states and animations
  2. Emulate sliding animations in both the functions with CSS transitions and dimensional properties
  3. Use sliding-up and sliding-down functions to create a toggle sliding function

JavaScript

It is pretty obvious that we need a function to bring about the effect. Also, we will need to specify a target and a trigger element to trigger the effect.

Is there anything else we need in this function? Animation speed!

So, we almost know the parameters needed to be supplied to our slideToggle function. Let’s break down our problem into three different blocks:

  1. slide-up
  2. slide-down
  3. slide-toggle

Here, I have broken down the problem into three different functions with target and animation-duration as their arguments.

let slideUp = (target, duration) => {
  /* Sliding-up logic */
}

let slideDown = (target, duration) => {
  /* Sliding-down logic */
}

let slideToggle = (target, duration) => {
  /* Slide-toggle logic */
}

Animation trigger should stay independent from the scope of the above functions. Why? This will keep things simple.


Sliding-up

Starting off, initialize the height and transition properties to streamline the sliding-up animation. I’m going to write just the statements to simplify the code for you. The complete code for this section should be there within the slideUp() function’s boundaries.

target.style.transitionProperty = 'height, margin, padding'; /* [1.1] */
target.style.transitionDuration = duration + 'ms'; /* [1.2] */
target.style.boxSizing = 'border-box'; /* [2] */
target.style.height = target.offsetHeight + 'px'; /* [3] */
  1. Add the CSS transition-property to our target. Apply it to the height, margin, and padding properties of the target. Then add the CSS transition-duration property to our target. This takes the duration argument we supply in the slideUP function.
  2. Make the element contain the padding and border (more about box-sizing reset)
  3. Get the net height of the target and use it to set the CSS height attribute

Now, the slideUp function should make the target element switch its state to hidden. Let’s zero down a bunch of CSS properties for that matter.

target.style.height = 0; /* [4] */
target.style.paddingTop = 0; /* [5.1] */
target.style.paddingBottom = 0; /* [5.2] */
target.style.marginTop = 0; /* [6.1] */
target.style.marginBottom = 0; /* [7.2] */
target.style.overflow = 'hidden'; /* [7] */
  1. Zero down the CSS height for the target
  2. Zero down the vertical padding i.e. padding-top and padding-bottom, if any
  3. Zero down the vertical margin i.e. margin-top and margin-bottom, if any
  4. Set the overflow property to hidden

Finally remove all the additional properties after the animation completes. This can be easily accomplished with the help of setTimeout function.

window.setTimeout( () => {
  target.style.display = 'none'; /* [8] */
  target.style.removeProperty('height'); /* [9] */
  target.style.removeProperty('padding-top');  /* [10.1] */ 
  target.style.removeProperty('padding-bottom');  /* [10.2] */ 
  target.style.removeProperty('margin-top');  /* [11.1] */ 
  target.style.removeProperty('margin-bottom');  /* [11.2] */ 
  target.style.removeProperty('overflow');  /* [12] */ 
  target.style.removeProperty('transition-duration');  /* [13.1] */ 
  target.style.removeProperty('transition-property');  /* [13.2] */ 
}, duration);
  1. Hide the target element by setting display to none
  2. Remove inline height property from the target
  3. Remove inline vertical padding properties from the target
  4. Remove inline vertical margin properties from the target
  5. Remove inline overflow properties from the target
  6. Remove inline transition properties from the target

And that’s our slideUp function written in plain JavaScript. Similarly, we’ll do the slideDown function in the next section.


Sliding-down

Again, we will initialize some CSS properties to keep up with the animation effect. But since this function is for sliding-down animation, we will also need to check a few things.

Note that this again is to be explicitly inside the slideDown function.

target.style.removeProperty('display'); /* [1] */
let display = window.getComputedStyle(target).display;
if (display === 'none') { /* [2] */
  display = 'block';
}
target.style.display = display;
  1. Remove the previously set CSS display property of the target, if any
  2. If by chance the current display property of the target is set to none, switch it to block

Make sure the initial state of our target element is hidden. Let’s add some more code in our slideDown function to ensure the hidden state of our element.

let height = target.offsetHeight; /* [3] */
target.style.height = 0; /* [4] */
target.style.paddingTop = 0; /* [5.1] */
target.style.paddingBottom = 0; /* [5.2] */ 
target.style.marginTop = 0; /* [6.1] */ 
target.style.marginBottom = 0; /* [6.2] */ 
target.style.overflow = 'hidden'; /* [7] */ 
  1. Store the current height of the element to use it further to slide-down
  2. Zero down the vertical padding for the target element
  3. Zero down the vertical margin for the target element
  4. Disable the element from letting anything flow outside of it

Now is the time to write the actual logic to slide the target element down. This can be done by adding the height and transition properties to the target element.

target.style.boxSizing = 'border-box'; /* [8] */
target.style.transitionProperty = "height, margin, padding";  /* [9.1] */ 
target.style.transitionDuration = duration + 'ms'; /* [9.2] */
target.style.height = height + 'px'; /* [10] */
target.style.removeProperty('padding-top'); /* [11.1] */ 
target.style.removeProperty('padding-bottom'); /* [11.2] */ 
target.style.removeProperty('margin-top'); /* [12.1] */ 
target.style.removeProperty('margin-bottom'); /* [12.2] */
  1. Make the element contain the padding and borders
  2. Apply transition properties to the height, margin, and padding of the target and then add transition-duration based on the duration argument supplied by the function
  3. Set the height property based on the height stored previously
  4. Remove the inline vertical padding properties
  5. Remove the inline vertical margin properties

Finally, let’s remove the unnecessary properties after the animation gets completed.

window.setTimeout( () => {
  target.style.removeProperty('height'); /* [13] */
  target.style.removeProperty('overflow'); /* [14] */
  target.style.removeProperty('transition-duration'); /* [15.1] */
  target.style.removeProperty('transition-property'); /* [15.2] */
}, duration);

Do the following things when the animation gets completed:

  1. Remove the inline height property
  2. Remove the inline overflow property
  3. Remove the inline transition properties

And that was our slideDown function. Pretty much like the slideUp.


Toggle between slideUp and slideDown

Toggling between slideUp and slideDown becomes pretty easy. Why? Because we just have to check the state of the element and then call the slideUp and slideDown functions accordingly.

let slideToggle = (target, duration = 500) => {
  if (window.getComputedStyle(target).display === 'none') {
    return slideDown(target, duration);
  } else {
    return slideUp(target, duration);
  }
}

The logic is pretty simple—if the target is hidden i.e. if its display is set to none, then do slideDown. Else slide it up. Easy-peasy!


Implementation

Finally, it’s time to see our pure JavaScript slideToggle in action. Begin with the sample CSS and HTML given below.

<style>
  #target {
     padding: 1.5em;
     margin: 1.5em;
     background-color: #eee;
  }
</style>
<div id="target">Testing JavaScript slideToggle</div>
<button id="trigger">slideToggle</button>

And then add this pinch of JavaScript after you add all the above discussed code, wrapped in the <script> tag, of course.

document.getElementById("trigger").addEventListener('click', function() {
  slideToggle(document.getElementById("target"), 200);
});

See the Live Demo

Nice and dandy. Isn’t it?


In conclusion

This doesn’t support the left and right support yet. I’ll surely be adding all that stuff in future. I hope this was helpful for you, as always, I await your suggestions through comments.