user934902
user934902

Reputation: 1204

Dynamically change div heights on slideToggle

enter image description hereThis is hard to explain so bear with me.

I have a column on my webpage that is divided into 2 panels/sections. When the user minimizes a panel, I want the secondary panel to expand to take up the remaining space.

When Panel 1 is minimized, the list hides, panel 2 header will move up directly underneath panel 1 and the list will expand to fill the rest of the column space below.

When Panel 2 is minimized, the list inside hides, panel 2 header will go directly to the bottom of the page and panel 1 list will expand to fit the rest of the column

Fiddle: http://jsfiddle.net/mL9RG/

I can get the lists inside the sections to slideToggle no problem. I can also add a fixed CSS height animation to kind of achieve what I am looking for. I have not had success with %'s to make it responsive.

HTML

<div id="wrapper">
    <div id="section1">
        <h2 id="crossbar1">Section 1<span id="plus2">+/-</span></h2>
        <ul id="list1">
            <li>Stuff</li>
            <li>Stuff</li>
            <li>Stuff</li>
            <li>Stuff</li>
        </ul>
    </div>
    <div id="section2">
        <h2 id="crossbar2">Section 2 <span id="plus2">+/-</span></h2>
        <ul id="list2">
            <li>Stuff</li>
            <li>More Stuff</li>
            <li>Stuff</li>
        </ul>
    </div>
</div>

CSS

#wrapper { width: 200px; height: 400px; margin: 0 auto; background-color: #eaeaea; }
ul { margin: 0; padding: 0; list-style: none; display: block; }
h2 { margin: 0; padding: 5px 0; background-color: #d8d8d8; position: relative; }
h2 span { position: absolute; right: 10px; font-size: 15px; top: 10px; cursor: pointer }
#section1 { 
    height: 50%; width: 100%; background-color: blue;
    box-sizing: border-box;
}
#section2 {
    height: 50%; width: 100%; background-color: green;
}

jquery

$('#crossbar1').click(function(){
   $('#list1').slideToggle(); 
});
$('#crossbar2').click(function(){
   $('#list2').slideToggle(); 
});

Upvotes: 1

Views: 2522

Answers (7)

Andi
Andi

Reputation: 234

Take a look at my solution with animating the height. The behavior should be similar to your images.

Modified css:

#wrapper { 
    width: 200px; height: 400px; margin: 0 auto; background-color: #eaeaea; 
    position: relative;
}
ul { margin: 0; padding: 0; list-style: none; display: block; }
h2 { margin: 0; padding: 5px 0; background-color: #d8d8d8; position: relative; }
h2 span { position: absolute; right: 10px; font-size: 15px; top: 10px; cursor: pointer }

#section1, #section2{
    box-sizing: border-box;
    width: 100%;
    height: 50%;
}

#section1 { 
    background-color: blue;
}
#section2 {
    background-color: green;
} 

Modified js

$('#crossbar1').click(function(){
    if($('#list1').is(':hidden')){
        if($('#list2').is(':hidden')){
            $( "#section1" ).animate({height: "362px"});
            $( "#section2" ).animate({height: "37px"});
        }else{   
            $( "#section1" ).animate({height: "50%"}); 
            $( "#section2" ).animate({height: "50%"});    
        } 
    }else{   
        if($('#list1').is(':hidden')){
            $( "#section1" ).animate({height: "362px"});
        }else{   
            $( "#section1" ).animate({height: "37px"}); 
            $( "#section2" ).animate({height: "362px"});     
        }  
    }
    $('#list1').slideToggle(); 
});
$('#crossbar2').click(function(){
    if($('#list2').is(':hidden')){  
        if($('#list1').is(':hidden')){
            $( "#section2" ).animate({height: "362px"});
        }else{   
            $( "#section2" ).animate({height: "50%"});
            $( "#section1" ).animate({height: "50%"});     
        } 
    }else{   
        if($('#list1').is(':hidden')){
            $( "#section2" ).animate({height: "362px"});
        }else{   
            $( "#section2" ).animate({height: "37px"}); 
            $( "#section1" ).animate({height: "362px"});     
        }  
    }
    $('#list2').slideToggle(); 
});

Check out the demo on jsFiddle. Hope that's what you'r looking for.

Edit: Small bugfix by closed section2 and opening and closing section1. (Updated jsFiddle-Demo)

Upvotes: 0

4dgaurav
4dgaurav

Reputation: 11496

HTML

<div class="wrapper">
  <div class="wrapper-item">
    Section 1
    <div class="type"></div>
  </div>
  <div class="data">
      <ul id="list1">
          <li>Stuff</li>
          <li>Stuff</li>
          <li>Stuff</li>
          <li>Stuff</li>
      </ul>
  </div>
   <div class="wrapper-item">
    Section 2
     <div class="type"></div>
  </div>
  <div class="data">
      <ul id="list1">
          <li>Stuff</li>
          <li>Stuff</li>
          <li>Stuff</li>
          <li>Stuff</li>
      </ul>
  </div>

CSS

body
{
font-family: Arial, Helvetica, sans-serif;
}
.wrapper
{
  width:100%;
  max-height:300px;
  margin:20px auto;
}
.wrapper-item {
font-size: 1em;
margin: 0 10px 0 10px;
padding: 10px;
height: 20px;
background: #f2f2f2;
border-bottom:1px solid #ccc;
color: #000;
cursor:pointer;
}

.wrapper-item.open
{
background:#14ad40;
border-bottom:0px;
color:#fff;
}
.wrapper-item.open .type {
float: right;
background: url('http://vivekarora.com/images/minus.png') center no-repeat;
padding: 10px;
}

.wrapper-item .type {
float: right;
background: url('http://vivekarora.com/images/plus.png') center no-repeat;
padding: 10px;
}

div.data {
background: #fff;
margin: 0 10px 0 10px;
padding: 10px;
border:1px solid #ccc;
font-size: .8em;
line-height: 140%;
display:none;
}

JS

$(function($) {
  var allWrappers = $('.wrapper div.data');
  var allWrapperItems = $('.wrapper .wrapper-item');
  $('.wrapper > .wrapper-item').click(function() {
    if($(this).hasClass('open'))
    {
      $(this).removeClass('open');
      $(this).next().slideUp("slow");
    }
    else
    {
    allWrappers.slideUp("slow");
    allWrapperItems.removeClass('open');
    $(this).addClass('open');
    $(this).next().slideDown("slow");
    return false;
    }
  });
});

DEMO

Upvotes: 1

vadimchin
vadimchin

Reputation: 1497

universal solution Fiddle

HTML

<div class="wrapper">
<div class="section expanded">
    <h2>Section 1<span class="toggle">+/-</span></h2>
    <ul class="list">
        <li>Stuff</li>
        <li>Stuff</li>
        <li>Stuff</li>
        <li>Stuff</li>
    </ul>
</div>
<div class="section expanded">
    <h2>Section 2<span class="toggle">+/-</span></h2>
    <ul class="list">
        <li>Stuff</li>
        <li>Stuff</li>
        <li>Stuff</li>
        <li>Stuff</li>
    </ul>
</div>
<div class="section expanded">
    <h2>Section 3<span class="toggle">+/-</span></h2>
    <ul class="list">
        <li>Stuff</li>
        <li>Stuff</li>
        <li>Stuff</li>
        <li>Stuff</li>
    </ul>
</div>

css

.wrapper {
    width: 200px; 
    height: 400px; 
    margin: 0 auto; 
    background-color: #eaeaea; 
    position: relative;
}


ul { 
    margin: 0; 
    padding: 0; 
    list-style: none; 
    display: block; 
}

h2 { 
    margin: 0; 
    padding: 0; 
    height:38px;
    line-height:38px; 
    background-color: #d8d8d8; 
    position: relative; 
}

h2 span { 
    position: absolute; 
    right: 10px; 
    font-size: 15px; 
    top: 0px; 
    cursor: pointer 
}

.section {
    position: relative;
    overflow:hidden;
}

script

function recalc(){
    var jq_parent = $(this).closest('.section');
    jq_parent.attr('class', 'section ' + (jq_parent.hasClass('expanded') ? 
                   'collapsed' : 'expanded'));
    var num = $('.expanded').length;
    if (num > 0) {
        var h = (400 - (38 * $('.collapsed').length) )/ num;
        $('.collapsed').animate({height:'38px'});
        $('.expanded').animate({height: h + 'px' });
    }
}
$('.toggle').click(recalc);
recalc();

Upvotes: 2

Jean-Paul
Jean-Paul

Reputation: 21170

What I suggest is you use jQuery's .toggleClass() function.

If you make two additional classes:

.open {
    height: 50%;    
}

.fullOpen {
    height: 100%;   
}

You can then control the states of the content boxes as follows:

$('#crossbar1').click(function(){
   $("#one").toggleClass('open', 100);
   $('#bodyOne').slideToggle(100); 
   $("#two").toggleClass('fullOpen', 100);   
});

$('#crossbar2').click(function(){
   $("#two").toggleClass('open', 100);
   $('#bodyTwo').slideToggle(100); 
   $("#one").toggleClass('fullOpen', 100);    
});

This works perfectly and does exactly as you implied.

DEMO

You can also achieve it by explicitly keeping track of the states of the boxes:

DEMO 2

Hope this helps you out!

Upvotes: 0

Zeta
Zeta

Reputation: 105885

You want to use flexboxes

I assume that you used the div#section in order to create the effect, and that you don't need them, which gives us cleaner markup and less nodes in the DOM:

<div id="wrapper">
    <h2 id="crossbar1">Section 1<span id="plus1">+/-</span></h2>
    <ul id="list1">
        <li>Stuff</li>
        <li>Stuff</li>
        <li>Stuff</li>
        <li>Stuff</li>
    </ul>
    <h2 id="crossbar2">Section 2 <span id="plus2">+/-</span></h2>
    <ul id="list2">
        <li>Stuff</li>
        <li>More Stuff</li>
        <li>Stuff</li>
    </ul>
</div>

Now we set #wrapper to be a flexbox (via display: flex), and say that we want to use it in a column fashion (flex-direction: column), that means, that the content will spread out vertically. Also, we set the flex-grow on the lists to 1, so that they will grow if necessary:

#wrapper { display: flex; flex-flow: column nowrap; 
           width: 200px; height: 400px; margin: 0 auto; background-color: #eaeaea; }
#wrapper > ul { flex-grow:1; margin: 0; padding: 0; list-style: none; }

/* your old rules */
h2 { margin: 0; padding: 5px 0; background-color: #d8d8d8; position: relative; }
h2 span { position: absolute; right: 10px; font-size: 15px; top: 10px; cursor: pointer }

#list1{background-color: blue}
#list2{background-color: green}

For the mutual behaviour, I've chosen an approach which is easy to extend to more sections:

var addToToggleGroup = (function(){
    var toggleState = []; // holds the state of the elements in the group

    var toggle = function(i, target) {
        toggleState[i] = !toggleState[i];

        // prevent that we toggle this element if it is the last

        if(toggleState.every(function(el){ return !el;})){
            toggleState[i] = !toggleState[i];
            return;
        } else {
            $(target).slideToggle();
        }
    }

    return function (el, target){
        var i = toggleState.length;
        toggleState.push(true);
        $(el).on("click", function(){
            toggle(i, target);
        })
    }
})();

// add the elements to the toggle group. We can use jQuery selectors here
addToToggleGroup("#crossbar1","#list1");
addToToggleGroup("#crossbar2","#list2");

And that's it (fiddle).

References

Upvotes: 0

headlikearock
headlikearock

Reputation: 695

It sounds like you are describing the behavior implemented by Bootstrap's collapse plugin, is that correct? See an example here: http://getbootstrap.com/javascript/#collapse

Upvotes: 0

Rakesh Kumar
Rakesh Kumar

Reputation: 2853

Try this approach to achieve what you want.

I have used class instead of id. it will make code very short.

HTML

<div id="wrapper">
    <div class="section">
        <h2 class="crossbar">Section 1<span id="plus2">+/-</span></h2>
        <ul id="list1">
            <li>Stuff</li>
            <li>Stuff</li>
            <li>Stuff</li>
            <li>Stuff</li>
        </ul>
    </div>
    <div class="section">
        <h2 class="crossbar">Section 2 <span id="plus2">+/-</span></h2>
        <ul id="list2">
            <li>Stuff</li>
            <li>More Stuff</li>
            <li>Stuff</li>
        </ul>
    </div>
</div>

CSS

#wrapper { width: 200px; height: 400px; margin: 0 auto; background-color: #eaeaea; }
ul { margin: 0; padding: 0; list-style: none; display: block; }
h2 { margin: 0; padding: 5px 0; background-color: #d8d8d8; position: relative; }
h2 span { position: absolute; right: 10px; font-size: 15px; top: 10px; cursor: pointer }
.section{ 
   width: 100%; 
    box-sizing: border-box;
}
#list1{ height: 150px; background-color: blue;}
#list2{ height: 150px; background-color: green;}

Script

$('h2.crossbar').on('click', function(){
    $(this).next('ul').slideToggle();
}); 

Fiddle Demo

Upvotes: 0

Related Questions