Here is an answer to my own question I came up with today after playing with IE8, FF, Chrome and Safari.
Let's say we want to fold and unfold a div element vertically (so we'll be talking only about the height for simplicity) with minimal possible JavaScript and no jQuery. Could be an inline error message box that you want to show and hide nicely on the page. The message box itself is elastic and you don't know its actual dimensions. There are two general problems to be solved here:
Problem #1: offsetHeight, clientHeight and scrollHeight properties are notoriously inconsistent across browsers. You can't rely on them especially when margin/padding/border are set to something other than 0 and when the CSS height is something other than default (i.e. when actually folding/unfolding the element). There is getComputedStyle(), but it's available only on sane browsers and not in IE. OTOH, IE's currentStyle property doesn't return computed values as you might expect (e.g. you may see "auto" as the height value). It turns out, with no margin/padding/border offsetHeight is reliable and compatible on all modern browsers.
Problem #2: the only way (that I know of) to animate your div element is to change style.height, which, however, doesn't include padding and border, and thus you can't animate the box down to 0 or start your animation from 0 for elements with non-zero padding and border. (On top of that, IE doesn't accept 0px as style.height, so the minimal size would be 1px anyway).
The solution to both problems is to wrap your box around with another div with default style, i.e. padding/margin/border all set to 0, which is the default style for a new div. The box you want to fold and unfold should be inside and it shouldn't have a margin setting, only padding and border if desired. This way, you will be able to (1) reliably determine the height of your elastic inner box via offsetHeight, and (2) animate the height of the outer box to and from 1 pixel.
So, let's say we want to animate this:
<div id="errmsg" style="display:none">
<div class="errmsg">
<div class="window-close" onClick="javascript:showErrMsg('', this.parentNode.parentNode)"></div>
<div id="errmsg.text"></div>
</div>
</div>
The box is invisible in the beginning. The "window-close" class is a small box on the right with a cross which is used to hide the box when clicked. Calling showErrMsg() with empty string folds the message box (see below).
My JavaScript code to animate this error message box is as follows (I'm also animating opacity which is omitted here for simplicity):
var ANIMATE_STEPS = 10;
var ANIMATE_DELAY = 20;
function _obj(obj, parent)
{
if (typeof obj == 'string')
obj = (parent ? parent : document).getElementById(obj);
return obj;
}
function _setStyleHeight(obj, h)
{
_obj(obj).style.height = Math.round(h) + 'px';
}
function _animateVertFold(obj, cur, by, lim)
{
obj = _obj(obj);
cur += by;
if (by < 0 && cur <= 1)
{
obj.style.display = 'none';
_setStyleHeight(obj, 1); // IE doesn't accept 0
}
else if (by > 0 && cur >= lim)
{
_setStyleHeight(obj, lim);
}
else
{
_setStyleHeight(obj, cur);
setTimeout('_animateVertFold(\'' + obj.id + '\', ' +
cur + ', ' + by + ', ' + lim + ')', ANIMATE_DELAY);
}
}
function _expandElem(obj)
{
obj = _obj(obj);
if (obj.style.display == 'none')
{
obj.style.overflow = 'hidden';
_setStyleHeight(obj, 1);
obj.style.display = 'block';
var innerDiv = obj.getElementsByTagName('div')[0]; // better way?
var h = innerDiv.offsetHeight;
if (h > 0)
_animateVertFold(obj, obj.offsetHeight, h / ANIMATE_STEPS, h);
}
}
function _shrinkElem(obj)
{
obj = _obj(obj);
if (!obj.style.display || obj.style.display != 'none')
{
obj.style.display = 'block';
obj.style.overflow = 'hidden';
var h = obj.offsetHeight;
_animateVertFold(obj, h, - h / ANIMATE_STEPS, h);
}
}
function showErrMsg(msg, id)
{
id || (id = '_errmsg');
obj = _obj(id);
if (msg != '')
{
_obj(id + '.text').innerHTML = msg;
_expandElem(obj);
}
else
_shrinkElem(obj);
}
You can call showErrMsg() and the code will take care of the rest. You will have to define the window-close class as something that mimics a GUI close-window button. And of course the "errmsg" class as a box with distinct background color, border and possibly font face as well.