Hi there,

I'm using this script for collapsible divs...

http://www.harrymaugans.com/2007/03/06/how-to-create-an-animated-sliding-collapsible-div-with-javascript-and-css/

Everything's working fine but I need to add an image for show/hide state. When the div is showing the header displays an "open" arrow...when closed it displays a "closed" arrow. Any help is really appreciated!! Thanks :)

Here's the JS...

var timerlen = 5;
var slideAniLen = 250;

var timerID = new Array();
var startTime = new Array();
var obj = new Array();
var endHeight = new Array();
var moving = new Array();
var dir = new Array();

function slidedown(objname){
        if(moving[objname])
                return;

        if(document.getElementById(objname).style.display != "none")
                return; // cannot slide down something that is already visible

        moving[objname] = true;
        dir[objname] = "down";
        startslide(objname);
}

function slideup(objname){
        if(moving[objname])
                return;

        if(document.getElementById(objname).style.display == "none")
                return; // cannot slide up something that is already hidden

        moving[objname] = true;
        dir[objname] = "up";
        startslide(objname);
}

function startslide(objname){
        obj[objname] = document.getElementById(objname);

        endHeight[objname] = parseInt(obj[objname].style.height);
        startTime[objname] = (new Date()).getTime();

        if(dir[objname] == "down"){
                obj[objname].style.height = "1px";
        }

        obj[objname].style.display = "block";

        timerID[objname] = setInterval('slidetick(\'' + objname + '\');',timerlen);
}

function slidetick(objname){
        var elapsed = (new Date()).getTime() - startTime[objname];

        if (elapsed > slideAniLen)
                endSlide(objname)
        else {
                var d =Math.round(elapsed / slideAniLen * endHeight[objname]);
                if(dir[objname] == "up")
                        d = endHeight[objname] - d;

                obj[objname].style.height = d + "%";
        }

        return;
}

function endSlide(objname){
        clearInterval(timerID[objname]);

        if(dir[objname] == "up")
                obj[objname].style.display = "none";

        obj[objname].style.height = endHeight[objname] + "%";

        delete(moving[objname]);
        delete(timerID[objname]);
        delete(startTime[objname]);
        delete(endHeight[objname]);
        delete(obj[objname]);
        delete(dir[objname]);

        return;
}

function toggleSlide(objname){
  if(document.getElementById(objname).style.display == "none"){
    // div is hidden, so let's slide down
    slidedown(objname);
  }else{
    // div is not hidden, so slide up
    slideup(objname);
  }
}

Davec,

There must be many ways to do this. Here's one, which is both practical and fun.

First wrap existing code in a contructor function to avoid putting lots of members in the global namespace.

Here it is with a few tweeks including the ability to specify callback function(s), which execute when Slider.slidedown and Slider.slideup terminate.

function Slider(objname){//Constructor pattern
    // *** private variables ***
	var timerlen = 5;
    var slideAniLen = 250;
    var timerID, startTime, moving, dir, obj, endHeight, heightUnit, callbackFn;
	try { obj = document.getElementById(objname); }
	catch(e) { obj = null; }
	if(obj){
		endHeight = parseInt(obj.style.height);
		heightUnit = obj.style.height.replace(String(endHeight), '') || 'px';
	}
    // *** private methods ***
    var startslide = function(){
        startTime = (new Date()).getTime();
        if(dir == "down"){ obj.style.height = "1px"; }
        obj.style.display = "block";
        timerID = setInterval(slidetick, timerlen);
		return false;
    }
    var slidetick = function(){
        var elapsed = (new Date()).getTime() - startTime;
        if (elapsed > slideAniLen) { endSlide(); }
        else {
            var d = Math.round(elapsed / slideAniLen * endHeight);
			if(dir === "up") { d = endHeight - d };
			obj.style.height = d + heightUnit;
        }
    }
    var endSlide = function(){
        clearInterval(timerID);
        if(dir == "up") { obj.style.display = "none"; }
        obj.style.height = endHeight + heightUnit;
		moving = false;
		if(callbackFn) { callbackFn(); }
    }
    // *** public methods ***
	this.slidedown = function(fn){
		if(!obj || moving || obj.style.display !== "none") { return false; }// cannot slide down something that is already moving or visible
        moving = true;
        dir = "down";
		callbackFn = (!fn) ? null : fn;
		startslide();
		return false;
    }
    this.slideup = function(fn){
        if(!obj || moving || obj.style.display === "none") { return false; }// cannot slide up something that is already moving or hidden
        moving = true;
        dir = "up";
		callbackFn = (!fn) ? null : fn;
		startslide();
		return false;
    }
    this.toggleSlide = function(fn){
	  fn = (!fn) ? null : fn;
      if(obj.style.display == "none") { return this.slidedown(fn); }// div is hidden, so let's slide down
      else { return this.slideup(fn); }// div is not hidden, so slide up
    }
}

So now we have a mechanism for swapping an image src (or other action) whenever a Slider object is opened or closed.

Getting your arrow to change is thus made very simple by calling Slider.slidedown , Slider.slideup with the name of an appropriate handler. This also works for Slider.toggleSlide because the toggleSlide method calls slidedown or sliderup and passes the calback function through to it.

slider.slidedown(swapButton);
slider.slideup(swapButton);
slider.toggleSlide(swapButton);

Here's a fully worked up XHTML document:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>Airshow :: Untitled</title>
<style type="text/css">
{}
</style>

<script>
function Slider(objID, imgID){//Constructor pattern
    // *** private variables ***
	var timerlen = 5;
    var slideAniLen = 250;
    var timerID, startTime, moving, dir, obj, img, endHeight, heightUnit, callbackFn;
	try { obj = document.getElementById(objID); }
	catch(e) { obj = null; }
	if(obj){
		endHeight = parseInt(obj.style.height);
		heightUnit = obj.style.height.replace(String(endHeight), '') || 'px';
	}
	try { img = document.getElementById(imgID); }
	catch(e) { img = null; }
    // *** private methods ***
    var startslide = function(){
        startTime = (new Date()).getTime();
        if(dir == "down"){ obj.style.height = "1px"; }
        obj.style.display = "block";
        timerID = setInterval(slidetick, timerlen);
    }
    var slidetick = function(){
        var elapsed = (new Date()).getTime() - startTime;
        if (elapsed > slideAniLen) { endSlide(); }
        else {
            var d = Math.round(elapsed / slideAniLen * endHeight);
			if(dir === "up") { d = endHeight - d };
			obj.style.height = d + heightUnit;
        }
    }
    var endSlide = function(){
        clearInterval(timerID);
        if(dir == "up") { obj.style.display = "none"; }
        obj.style.height = endHeight + heightUnit;
		moving = false;
		if(callbackFn) { callbackFn(obj, img, dir); }
    }
    // *** public methods ***
	this.slidedown = function(fn){
		if(!obj || moving || obj.style.display !== "none") { return false; }// cannot slide down something that is already moving or visible
        moving = true;
        dir = "down";
		callbackFn = (!fn) ? null : fn;
		startslide();
    }
    this.slideup = function(fn){
        if(!obj || moving || obj.style.display === "none") { return false; }// cannot slide up something that is already moving or hidden
        moving = true;
        dir = "up";
		callbackFn = (!fn) ? null : fn;
		startslide();
    }
    this.toggleSlide = function(fn){
	  fn = (!fn) ? null : fn;
      if(obj.style.display == "none") { return this.slidedown(fn); }// div is hidden, so let's slide down
      else { this.slideup(fn); }// div is not hidden, so slide up
    }
}

onload = function(){
	var slider = new Slider('myDiv', 'myImg');
	imgSrcs = {
		open: 'http:\/\/www.daniweb.com/rxrvbimages/statusicon/thread_dot.gif',//Change this to the url of the open arrow
		close: 'http:\/\/www.daniweb.com/rxrvbimages/statusicon/thread_dot_new.gif'//Change this to the url of the close arrow
	};

	function swapImg(obj, img, dir){
		if(img && dir){
			img.src = (dir === 'up') ? imgSrcs.open : imgSrcs.close;
		}
	}
	
	var downButton   = document.getElementById('downButton');
	var upButton     = document.getElementById('upButton');
	var toggleButton = document.getElementById('toggleButton');

	downButton.onclick = function(){
		slider.slidedown(swapImg);//open with callback
		return false;//suppress default click action
	};
	upButton.onclick = function(){
		slider.slideup(swapImg);//close with callback
		return false;//suppress default click action
	};
	toggleButton.onclick = function(){
		slider.toggleSlide(swapImg);//close with callback
		return false;//suppress default click action
	};
}
</script>
</head>

<body>

<img id="myImg" src="http://www.daniweb.com/rxrvbimages/statusicon/thread_dot.gif" border="0" /><br />
<div id="myDiv" style="display:none; overflow:hidden; height:50px;"><h3>This is a test!<br>Can you see me?</h3></div>

<a id="downButton" href="">Slide Down</a>&nbsp;|&nbsp;
<a id="upButton" href="">Slide Up</a>&nbsp;|&nbsp;
<a id="toggleButton" href="">Toggle</a> (using Slider constructor)

</body>
</html>

You will see that we attach onclick handlers to the controls in javascript rather than HTML as is generally considered good practice.

Airshow

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.