1

I have 4 dropdown containers. When i am clicking on a header , i want its associated paragraph to appear and other paragraph, that had been appeared ,disappear.

When a header is clicked, i remove active class from all the other paragraphs and add it to the paragraph that its header is clicked. It works fine but the problem is that first the current paragraph appears and then other paragraph disappears but i want them to work synchronously like while one appears another disappears but i do not know how to do that.

HTML:

<div class="dropDown">
  <div class="header">
    <a href="javascript:void(0)">header1</a>
  </div>
  <p class="active">some things here some things here some things here some things here</p>
</div>

<div class="dropDown">
  <div class="header">
    <a href="javascript:void(0)">header2</a>
  </div>
  <p>some things here some things here some things here some things here</p>
</div>

<div class="dropDown">
  <div class="header">
    <a href="javascript:void(0)">header3</a>
  </div>
  <p>some things here some things here some things here some things here</p>
</div>

<div class="dropDown">
  <div class="header">
    <a href="javascript:void(0)">header4</a>
  </div>
  <p>some things here some things here some things here some things here</p>
</div>

CSS:

.dropDown p{
  background: rgb(245, 245, 245);
  border-right: 40px solid #e8e8e8;
  font-size: 13px;
  line-height: 35px;
  max-height: 0px;
  overflow: hidden;
  margin-bottom: 0;
  padding: 0px 15px 0px 30px;
  transition: max-height .3s ease;
}

.dropDown p.active{
  max-height: 500px;
  padding-top:8px;
  padding-bottom: 20px;
}

jQuery:

Headers.click(function(){
  var theP = $(this).parent().children("p"); //current paragraph
  dropDownParagrsphs.not(theP).removeClass("active");
  theP.toggleClass("active");
});

How can i make the transitions to work together like while one paragraph's height decreases , other paragraph's height increases?

Code-Lover
  • 299
  • 1
  • 14

2 Answers2

3

interestingly, you've stumbled on a deceptively difficult problem in pure CSS. The truth is, your paragraphs are already behaving as you want them to, the problem is that you've specified a large max height relative to the actual content of the p, it gives the impression that they are executed one after the other, but that's just because the time it takes is relatively (compared to actual height of p with overflow: hidden) long to grow/shrink max-height to 500px. It's as if you have an invisible box growing to 500px. This should be easily solvable by changing your max-height to auto, but unfortunately you cannot animate height auto in pure CSS transitions. your options are:

a) choose a different hardcoded max-height which is closer to the actual content size.
b) use transform scale(Y)
c) use pure JS: for example slideUp and slideDown

var Headers = $('.header') 
var dropDownParagraphs = $('.dropDown p') 

Headers.click(function(){
  var theP = $(this).parent().children("p"); //current paragraph
    // theP.addClass("active");
  // dropDownParagraphs.not(theP).removeClass("active");
  dropDownParagraphs.not(theP).slideUp(200);
  theP.slideDown(200);

});

check this codepen for implementation of c) https://codepen.io/bakuthe3rd/pen/abvzVJz

baku
  • 765
  • 8
  • 22
1

You can use .slideToogle(duration, easing, callback) method to do so. Also, I've shifted padding-bottom and padding-top properties to p from p.active, so that they don't change dramatically during the transition.

jQuery:

$('p').slideUp(0); // close all
$('.active').slideToggle(300); // open the default

$('.header').click(function() {
  var nowP = $(this).parent().children("p"); // current paragraph
  var prevP = $('.active'); // opened paragraph
  var isSame = $('p').index(prevP) == $('p').index(nowP);

  prevP.removeClass("active").slideToggle({ duration: 300, queue: false });
  if (!isSame) nowP.addClass("active").slideToggle({ duration: 300, queue: false });
});

$('p').slideUp(0);
$('.active').slideToggle(300);

$('.header').click(function() {
  var nowP = $(this).parent().children("p"); // current paragraph
  var prevP = $('.active'); // opened paragraph
  var isSame = $('p').index(prevP) == $('p').index(nowP);

  prevP.removeClass("active").slideToggle({ duration: 300, queue: false });
  if (!isSame) nowP.addClass("active").slideToggle({ duration: 300, queue: false });
});
.dropDown p {
  background: rgb(245, 245, 245);
  border-right: 40px solid #e8e8e8;
  font-size: 13px;
  line-height: 35px;
  overflow: hidden;
  margin-bottom: 0;
  padding: 0px 15px 0px 30px;
  transition: max-height .3s ease;
  padding-top: 8px;
  padding-bottom: 20px;
}

.dropDown p.active {
  max-height: 500px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="dropDown">
  <div class="header">
    <a href="javascript:void(0)">header1</a>
  </div>
  <p class="active">some things here some things here some things here some things here</p>
</div>

<div class="dropDown">
  <div class="header">
    <a href="javascript:void(0)">header2</a>
  </div>
  <p>some things here some things here some things here some things here</p>
</div>

<div class="dropDown">
  <div class="header">
    <a href="javascript:void(0)">header3</a>
  </div>
  <p>some things here some things here some things here some things here</p>
</div>

<div class="dropDown">
  <div class="header">
    <a href="javascript:void(0)">header4</a>
  </div>
  <p>some things here some things here some things here some things here</p>
</div>

Also, you'll notice that on clicking the same link, the dropdown will be closed. I've added this as I expect this is probably the desired effect :)

Further, you can add 'linear' as second argument in .slideToggle method, if you need a linear transition. By default, it is 'swing'.

vrintle
  • 5,501
  • 2
  • 16
  • 46
  • 1
    @Code-Lover: Just to inform you that to [run animations synchronously](https://stackoverflow.com/questions/1251300/how-to-run-two-jquery-animations-simultaneously), your argument to `.slideToggle` should be `{duration: 300, queue: false}`. Without it, those animations will _never be_ synchronous. However, to our eyes, it appears as it is. I guess, I got a bit late in editing my answer. Further, the answer you'd selected doesn't talk about this anywhere, so I doubt if that is valid or not. – vrintle Apr 12 '20 at 03:01
  • 1
    you have misunderstood the jquery queue. The animate queue does not, by default, act globally across multiple elements. the default queue is for synchronicity on the same element. The only effect toggling the queue would have in this situation, that would be noticeable, is if the duration is very long, and the user is re-toggling the same, or different drawer, before the currently rendering animation has time to complete. You can check this updated codepen for a clearer visual example. https://codepen.io/bakuthe3rd/pen/GRpJKGp – baku Apr 12 '20 at 03:28
  • @baku: thanks for the example. It seems that using `queue: false` just don't queue animations, it releases them instantly to let them animate. So, I think that the post I've mentioned is incomplete somehow, what do you think? Also, when running your original code (in your answer), when I tap headers (apart from header4), the header4 doesn't remain at original height sometime, it glitches ~1px. Btw, have you edited the original code, by mistake? – vrintle Apr 12 '20 at 03:51
  • Thank you for your useful answer. I meant how to make it the way that appear synchronous. I accepted that answer because it explained why my solution does not work – Code-Lover Apr 12 '20 at 23:34