JavaScript slidedown without jQuery

As an improvement to @Ruben Serrate solution which misses the use case for unknown height, I've created this using CSS3 and javascript (no jQuery):

/**
* getHeight - for elements with display:none
 */
getHeight = function(el) {
    var el_style      = window.getComputedStyle(el),
        el_display    = el_style.display,
        el_position   = el_style.position,
        el_visibility = el_style.visibility,
        el_max_height = el_style.maxHeight.replace('px', '').replace('%', ''),

        wanted_height = 0;


    // if its not hidden we just return normal height
    if(el_display !== 'none' && el_max_height !== '0') {
        return el.offsetHeight;
    }

    // the element is hidden so:
    // making the el block so we can meassure its height but still be hidden
    el.style.position   = 'absolute';
    el.style.visibility = 'hidden';
    el.style.display    = 'block';

    wanted_height     = el.offsetHeight;

    // reverting to the original values
    el.style.display    = el_display;
    el.style.position   = el_position;
    el.style.visibility = el_visibility;

    return wanted_height;
};


/**
* toggleSlide mimics the jQuery version of slideDown and slideUp
* all in one function comparing the max-heigth to 0
 */
toggleSlide = function(el) {
    var el_max_height = 0;

    if(el.getAttribute('data-max-height')) {
        // we've already used this before, so everything is setup
        if(el.style.maxHeight.replace('px', '').replace('%', '') === '0') {
            el.style.maxHeight = el.getAttribute('data-max-height');
        } else {
            el.style.maxHeight = '0';
        }
    } else {
        el_max_height                  = getHeight(el) + 'px';
        el.style['transition']         = 'max-height 0.5s ease-in-out';
        el.style.overflowY             = 'hidden';
        el.style.maxHeight             = '0';
        el.setAttribute('data-max-height', el_max_height);
        el.style.display               = 'block';

        // we use setTimeout to modify maxHeight later than display (to we have the transition effect)
        setTimeout(function() {
            el.style.maxHeight = el_max_height;
        }, 10);
    }
}

Here is the demo: http://jsfiddle.net/pgfk2mvo/

Please let me know if you can find any improvements to this as I always try to improve my code. Happy coding ! :D


can be done in plain JavaScript. Just harder.

Actually it's not too hard. You just need to get comfortable with setTimeout() (which is a good idea anyway since it teaches you the programming style of node.js). The most bare-bones implementation (does not have all of jQuery's features, that's left as homework for the reader):

function slideDown (element, duration, finalheight, callback) {
    var s = element.style;
    s.height = '0px';

    var y = 0;
    var framerate = 10;
    var one_second = 1000;
    var interval = one_second*duration/framerate;
    var totalframes = one_second*duration/interval;
    var heightincrement = finalheight/totalframes;
    var tween = function () {
        y += heightincrement;
        s.height = y+'px';
        if (y<finalheight) {
            setTimeout(tween,interval);
        }
    }
    tween();
}

Of course, that's not the shortest possible way to write it and you don't have to declare all those variables like one_second etc. I just did it this way for clarity to show what's going on.

This example is also shorter and easier to understand than trying to read jQuery's source code.


Has anyone done something like this or any effects just using plain JavaScript?

Oh yeah, sure, it's the sort of thing I do for fun on weekends:

  • http://slebetman.110mb.com/tank3.html (hint: units are clickable)
  • http://slebetman.110mb.com/jsgames/freakout

Since we are in 2014, why not use CSS transitions and just change the height property of the element? Fiddle

CSS:

.wrapper {
    transition:height 1s ease-out;
    height:0;
    overflow:hidden;
}

HTML:

<div id="wrapper">
//content
</div>

JAVASCRIPT:

document.getElementById("wrapper").style.height = //content height +"px";

2020 EDIT (dealing with unknown height):

So we're in 2020 and it's even more obvious now we should rely on CSS effects for this kind of animations.

However, a valid point has been made against this answer - you need to specify the height of the element that you're animating in the js code, and you might not know this value in advance.

So six years later, I'm adding a couple extra lines of code to cover this case.

So if we use the same CSS and HTML as in our old 2014 example, this is the new JS. New Fiddle!

const slideDown = elem => elem.style.height = `${elem.scrollHeight}px`;

slideDown(document.getElementById("wrapper"));


Here is a nice little piece of code I wrote from scratch.
It is purely time based.

var minheight = 20;
var maxheight = 100;
var time = 1000;
var timer = null;
var toggled = false;

window.onload = function() {
    var controller = document.getElementById('slide');
    var slider = document.getElementById('slider');
    slider.style.height = minheight + 'px'; //not so imp,just for my example
    controller.onclick = function() {  
        clearInterval(timer);
        var instanceheight = parseInt(slider.style.height);  // Current height
        var init = (new Date()).getTime(); //start time
        var height = (toggled = !toggled) ? maxheight: minheight; //if toggled

        var disp = height - parseInt(slider.style.height);
        timer = setInterval(function() {
            var instance = (new Date()).getTime() - init; //animating time
            if(instance <= time ) { //0 -> time seconds
                var pos = instanceheight + Math.floor(disp * instance / time);
                slider.style.height =  pos + 'px';
            }else {
                slider.style.height = height + 'px'; //safety side ^^
                clearInterval(timer);
            }
        },1);
    };
};

Test it here: http://jsbin.com/azewi5/5

Tags:

Javascript