Help - Search - Members - Calendar
Full Version: Classic Div Toggle
HTMLHelp Forums > Programming > Client-side Scripting
Duchess
I have the following:

CODE
function showdiv(name,focus){
var obj = (document.getElementById)? document.getElementById(name) : eval("document.all[name]");
    if (obj.style.display=="none"){
        obj.style.display="";
    }else{
        obj.style.display="none";
    }
}

<div align="left"><a onClick="showdiv('div1');" style="cursor: pointer;">DIV 1</a></div>
<div id="div1" style="display: none;">DIV CONTENT</div>

<div align="left"><a onClick="showdiv('div2');" style="cursor: pointer;">DIV 2</a></div>
<div id="div2" style="display: none;">DIV CONTENT</div>


This is the classic JavaScript solution to toggling a div, and it handles multiple divs. It works fine.

What I'm trying to do is advance it a level by making it so only one div shows at a time. In otherwords, I click the link, it shows the div. I click it again, it goes away. This works. I click that link again, it shows the div again. I click the other link now, the first div goes away and the second div shows up instead. So on and so forth.

I tried doing this:

CODE
function showdiv(name1,name2,focus){
var obj1 = (document.getElementById)? document.getElementById(name1) : eval("document.all[name1]");
var obj2 = (document.getElementById)? document.getElementById(name2) : eval("document.all[name2]");
    if (obj2.style.display==""){
        obj2.style.display="none";
    }
    if (obj.style.display=="none"){
        obj.style.display="";
    }else{
        obj.style.display="none";
    }
}

<div align="left"><a onClick="showdiv('div1,div2');" style="cursor: pointer;">DIV 1</a></div>
<div id="div1" style="display: none;">DIV CONTENT</div>

<div align="left"><a onClick="showdiv('div2,div1');" style="cursor: pointer;">DIV 2</a></div>
<div id="div2" style="display: none;">DIV CONTENT</div>


Is this just too simple to be true?
Christian J
QUOTE(Duchess @ Jan 6 2010, 10:26 PM) *

CODE
function showdiv(name,focus)


The "focus" argument doesn't seem to be used for anything.

QUOTE
CODE
var obj = (document.getElementById)? document.getElementById(name) : eval("document.all[name]");

That looks terribly ancient, I recall document.all was necessary for IE4. Today "all" browsers support document.getElementById.

QUOTE

What I'm trying to do is advance it a level by making it so only one div shows at a time. In otherwords, I click the link, it shows the div. I click it again, it goes away. This works. I click that link again, it shows the div again. I click the other link now, the first div goes away and the second div shows up instead. So on and so forth.

I rewrote both the script and the HTML. ninja.gif This way it's meant to work with any number of DIVs. Haven't tested it much, though. I don't use link elements for the labels, since such links tend to annoy users without javascript, instead I create BUTTON elements (with javascript) so that users can open the DIVs with their keyboard too (not just the mouse). If you don't like the look of buttons you might change their appearance with CSS. The script must appear after the DIVs.


CODE
<div class="toggle">DIV 1
    <div>DIV CONTENT 1</div>
</div>

<div class="toggle">DIV 2
    <div>DIV CONTENT 2</div>
</div>

<script type="text/javascript">
function toggle(parent)
{
    for(var i=0; i<div.length; i++)
    {
        if(div[i].className=='toggle')
        {
            // make toggle button:
            if(div[i].firstChild.nodeName!='BUTTON')
            {
                var button=document.createElement('button');
                button.setAttribute('type', 'button');
                button.appendChild(div[i].firstChild);
                div[i].insertBefore(button, div[i].firstChild);
            }

            var child=div[i].getElementsByTagName('div')[0];
            
            // mark the parent of an opened DIV:
            div[i].state='';
            if(child.style.display=='block')
            {
                div[i].state='open';
            }
            
            child.style.display='none';
            
            div[i].onclick=function()
            {
                toggle(this);
            }
        }
    }
    
    if(parent)
    {
        var child=parent.getElementsByTagName('div')[0];
        if(child.style.display=='none' && parent.state!='open')
        {
            child.style.display='block';
        }
        else
        {
            child.style.display='none';
        }
    }
}
var div=document.getElementsByTagName('div');
toggle();
</script>
Duchess
First, thank you very much for your reply and help. smile.gif

QUOTE(Christian J @ Jan 7 2010, 08:22 AM) *

QUOTE(Duchess @ Jan 6 2010, 10:26 PM) *

CODE
function showdiv(name,focus)


The "focus" argument doesn't seem to be used for anything.

QUOTE
CODE
var obj = (document.getElementById)? document.getElementById(name) : eval("document.all[name]");

That looks terribly ancient, I recall document.all was necessary for IE4. Today "all" browsers support document.getElementById.


Could be. I had gotten this off another tutorial about two years ago for a different site I was working on. I figured, since it worked, why reinvent the wheel? The tutorial might have been rather old. The 'focus' argument was in that.

QUOTE(Christian J @ Jan 7 2010, 08:22 AM) *

CODE
<div class="toggle">DIV 1
    <div>DIV CONTENT 1</div>
</div>

<div class="toggle">DIV 2
    <div>DIV CONTENT 2</div>
</div>



I'm curious here about the nested divs. Do I really need the double tags here? Seems rather redundant to someone still learning.
Christian J
QUOTE(Duchess @ Jan 7 2010, 03:57 PM) *

I'm curious here about the nested divs. Do I really need the double tags here? Seems rather redundant to someone still learning.

I guess there are several ways to do it, but you'll at least need one element for the content you want to show/hide, and another for the clickable "label". You don't have to use DIV elements for this, many other elements might do as well (a definition list, perhaps?). Also you don't have to nest them, e.g. you could use a clickable heading followed by a text paragraph that's shown/hidden. Of course any such changes to the HTML require rewriting of the script as well.
Duchess
Okay. I can understand that.

QUOTE(Christian J @ Jan 7 2010, 08:22 AM) *

If you don't like the look of buttons you might change their appearance with CSS.


This is apparently the fun part. The buttons clash horribly with the template and the links looked much better. So, now that we've made them into buttons, how do I change them back to looking like links? So far, Google's turned up results for actual 'input' buttons for forms or how to make text links look like buttons, unless I'm searching badly. I tried a few and ended up with some odd results.
Christian J
QUOTE(Duchess @ Jan 7 2010, 09:27 PM) *

now that we've made them into buttons, how do I change them back to looking like links?

Like this?

CODE
button {
border: 0;
text-decoration: underline;
color: #00f;
background: #fff;
cursor: pointer;
}


Or you could simply remove the part of the script that creates the buttons, since it's clicking the parent DIV (and its content) that actually makes things happen. I'd rather keep the BUTTON elements though, since that enables keyboard navigation.
Duchess
QUOTE(Christian J @ Jan 7 2010, 05:56 PM) *
Or you could simply remove the part of the script that creates the buttons, since it's clicking the parent DIV (and its content) that actually makes things happen. I'd rather keep the BUTTON elements though, since that enables keyboard navigation.


I'm doing this for someone else's site. What they want goes. ^^;

Thanks again, though!
Christian J
You're welcome! smile.gif

Here's an idea that adds links instead of buttons. Use it instead of the button section in the script above.

CODE
// make toggle link
if(div[i].firstChild.nodeName!='A')
{
    var a=document.createElement('a');
    a.setAttribute('href', '#show_or_hide_contents');
    a.appendChild(div[i].firstChild);
    div[i].insertBefore(a, div[i].firstChild);
}


you may also want add a "return false" in the onclick to cancel the link's HREF:

CODE
div[i].onclick=function()
{
    toggle(this);
    return false;
}


By using "return false" like this you may even leave the HREF blank:

CODE
a.setAttribute('href', '');
Duchess
QUOTE(Christian J @ Jan 8 2010, 09:47 AM) *
Here's an idea that adds links instead of buttons. Use it instead of the button section in the script above.

CODE
// make toggle link
if(div[i].firstChild.nodeName!='A')
{
    var a=document.createElement('a');
    a.setAttribute('href', '#show_or_hide_contents');
    a.appendChild(div[i].firstChild);
    div[i].insertBefore(a, div[i].firstChild);
}


you may also want add a "return false" in the onclick to cancel the link's HREF:

CODE
div[i].onclick=function()
{
    toggle(this);
    return false;
}


By using "return false" like this you may even leave the HREF blank:

CODE
a.setAttribute('href', '');


Ooo... that looks fun. smile.gif I'll give it a go.
kit
This works beautifully. Thank you!
Is there a way to show the contents of div 1 from the start, before any of the buttons or links are clicked?
kit
I entered the first div without the toggle class above the rest so it appears at the start. Thank you again.
Christian J
QUOTE(kit @ Jun 16 2011, 03:30 PM) *

I entered the first div without the toggle class above the rest so it appears at the start. Thank you again.

Of course then it will not disappear even when the other DIVs are shown.

Here's yet another quick version (might be buggy):

CODE
<div class="toggle initially_open">DIV 1
    <div>DIV CONTENT 1</div>
</div>

<div class="toggle">DIV 2
    <div>DIV CONTENT 2</div>
</div>

<div class="toggle">DIV 3
    <div>DIV CONTENT 3</div>
</div>

<script type="text/javascript">
var mechanism='link'; // choose between 'link' or 'button'


function toggle(parent)
{
    for(var i=0; i<div.length; i++)
    {
        if(div[i].className.indexOf('toggle')!=-1)
        {
            // initialize menu
            if(!parent)
            {
                // build toggle mechanism (button or link)
                if(mechanism=='button')
                {
                    var button=document.createElement('button');
                    button.setAttribute('type', 'button');
                    button.appendChild(div[i].firstChild);
                    div[i].insertBefore(button, div[i].firstChild);
                }
                else if(mechanism=='link')
                {
                    var button=document.createElement('a');
                    button.setAttribute('href', '#show_or_hide_contents');
                    button.appendChild(div[i].firstChild);
                    div[i].insertBefore(button, div[i].firstChild);
                }

                // hide items onload, unless except those
                // flagged with the class "initially_open"
                if(div[i].className.indexOf('initially_open')==-1)
                {
                    div[i].getElementsByTagName('div')[0].style.display='none';
                }

                // detect clicks
                button.onclick=function()
                {
                    toggle(this.parentNode);
                    return false;
                }
            }

            // hide all items except the one clicked
            else if(parent)
            {
                div[i].getElementsByTagName('div')[0].style.display='none';
            }
        }
    }

    // react to clicks
    if(parent)
    {
        var child=parent.getElementsByTagName('div')[0];
        if(child.style.display=='none')
        {
            child.style.display='block';
        }
        else
        {
            child.style.display='none';
        }
    }
}
var div=document.getElementsByTagName('div');
toggle();
</script>

Christian J
Post moved here: http://forums.htmlhelp.com/index.php?s=&am...ost&p=72701
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Invision Power Board © 2001-2014 Invision Power Services, Inc.