user1610904
user1610904

Reputation: 397

Jquery Accordion - Make content open with animation

I have this JSfiddle: https://jsfiddle.net/07fdq3t1/5/

It's almost perfect, except I'd like to have it to where the content is displayed in an animated fashion. Right now it just pops open, but I'd like to have it slowly open. The animation for making it go to the top of the viewport is working great, but I need it to where when someone clicks an element, the content opens up slowly.

Any ideas?

<script>
$(document).ready(function() {
    $('.accordion').click(function(){
        if($(this).next('.container').is(':visible')) {
            $(this).removeClass('show');
            $(this).next('.container').slideUp();
        }
        else {
            $('.accordion').find('.container:visible').slideUp();
            $('.accordion').removeClass('show');
            $(this).addClass('show');
            $(this).next('.container').slideDown();
            $('html, body').animate({
                scrollTop: $(this).offset().top
            }, 200);
        }         
    });
});
</script>

<div class="accordion">Heading<span></span></div>
    <div class="container">
        <div class="content">
            <div>Sample Content</div>
            <p>Content here....</p>
        </div>
    </div>
<div class="accordion">Heading<span></span></div>
    <div class="container">
        <div class="content">
            <div>Sample Content</div>
            <p>Content here....</p>
        </div>
    </div>

Upvotes: 1

Views: 3825

Answers (3)

Tahir Ahmed
Tahir Ahmed

Reputation: 5737

All right. Here is my attempt in solving your problem. Have a look at the fiddle or snippet below.

JSFiddle.

Snippet:

var accordions=null;
var containers=null;
var slideDuration=400;
var scrollDuration=400;
var body=null;
var classNameShow='show';
var classNameAccordion='.accordion';
var classNameContainer='.container';
var dataOffsetTop='offsetTop';
$(document).ready(function(){
	body=$('html, body');
	accordions=$(classNameAccordion);
	containers=$(classNameContainer);
	accordions.each(function(){
		var openedAccordions=accordions.filter(function(){return $(this).hasClass(classNameShow);});
		openedAccordions.removeClass(classNameShow);
		$(this).data(dataOffsetTop,$(this).offset().top);
		openedAccordions.next(classNameContainer).stop(true).slideDown({
			duration:slideDuration,
			complete:function(){openedAccordions.addClass(classNameShow);}
		});
	});
	accordions.click(function(){
		var currentAccordion=$(this);
		var currentContainer=$(this).next(classNameContainer);
		var openedAccordions=accordions.filter(function(){return $(this).hasClass(classNameShow);});
		if(!currentAccordion.hasClass(classNameShow)){
			currentContainer.stop(true).slideDown({
				duration:slideDuration,
				complete:function(){currentAccordion.addClass(classNameShow);}
			});
			body.stop(true).animate({scrollTop:currentAccordion.data(dataOffsetTop)},scrollDuration);
		}else{
			currentContainer.stop(true).slideUp({
				duration:slideDuration,
				complete:function(){currentAccordion.removeClass(classNameShow);}
			});
		}
		containers.not(currentContainer).stop(true).slideUp({
			duration:slideDuration,
			complete:function(){openedAccordions.removeClass(classNameShow);}
		});
	});
});
.accordion {
	margin: 0;
	padding: 10px;
	height: 20px;
	border-top: #f0f0f0 1px solid;
	background: #cccccc;
	font-family: Arial, Helvetica, sans-serif;
	text-decoration: none;
	text-transform: uppercase;
	color: #000;
	font-size: 1em;
}
.accordion span {
	background: #000;
	color: #fff;
}
.show span {
	display: block;
	float: right;
	padding: 10px;
}
.accordion span {
	display: block;
	float: right;
	background: url(http://www.snyderplace.com/demos/images/plus.png) center center no-repeat;
	padding: 10px;
}
.show span {
	background: url(http://www.snyderplace.com/demos/images/minus.png) center center no-repeat;
}
div.container {
	padding: 0;
	margin: 0;
	display: none;
}
div.content {
	background: #f0f0f0;
	margin: 0;
	padding: 10px;
	font-size: .9em;
	line-height: 1.5em;
	font-family: "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif;
}
div.content ul, div.content p {
	margin: 0;
	padding: 3px;
}
div.content ul li {
	list-style-position: inside;
	line-height: 25px;
}
div.content ul li a {
	color: #555555;
}
div.show + .container {
	display: block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div class="accordion">Heading<span></span></div>
<div class="container">
	<div class="content">
		<div>Sample Content</div>
		<p>Content here....</p></div>
</div>
<div class="accordion">Heading<span></span></div>
<div class="container">
	<div class="content">
		<div>Sample Content</div>
		<p>Content here....</p>
	</div>
</div>
<div class="accordion show">Heading<span></span></div>
<div class="container">
	<div class="content">
		<div>Sample Content</div>
		<p>Content here....</p>
	</div>
</div>
<div class="accordion">Heading<span></span></div>
<div class="container">
	<div class="content">
		<div>Sample Content</div>
		<p>Content here....</p>
	</div>
</div>
<div class="accordion">Heading<span></span></div>
<div class="container">
	<div class="content">
		<div>Sample Content</div>
		<p>Content here....</p>
	</div>
</div>
<div class="accordion">Heading<span></span></div>
<div class="container">
	<div class="content">
		<div>Sample Content</div>
		<p>Content here....</p>
	</div>
</div>
<div class="accordion">Heading<span></span></div>
<div class="container">
	<div class="content">
		<div>Sample Content</div>
		<p>Content here....</p>
	</div>
</div>
<div class="accordion">Heading<span></span></div>
<div class="container">
	<div class="content">
		<div>Sample Content</div>
		<p>Content here....</p>
	</div>
</div>
<div class="accordion">Heading<span></span></div>
<div class="container">
	<div class="content">
		<div>Sample Content</div>
		<p>Content here....</p>
	</div>
</div>
<div class="accordion">Heading<span></span></div>
<div class="container">
	<div class="content">
		<div>Sample Content</div>
		<p>Content here....</p>
	</div>
</div>
<div class="accordion">Heading<span></span></div>
<div class="container">
	<div class="content">
		<div>Sample Content</div>
		<p>Content here....</p>
	</div>
</div>
<div class="accordion">Heading<span></span></div>
<div class="container">
	<div class="content">
		<div>Sample Content</div>
		<p>Content here....</p>
	</div>
</div>
<div class="accordion">Heading<span></span></div>
<div class="container">
	<div class="content">
		<div>Sample Content</div>
		<p>Content here....</p>
	</div>
</div>
<div class="accordion">Heading<span></span></div>
<div class="container">
	<div class="content">
		<div>Sample Content</div>
		<p>Content here....</p>
	</div>
</div>
<div class="accordion">Heading<span></span></div>
<div class="container">
	<div class="content">
		<div>Sample Content</div>
		<p>Content here....</p>
	</div>
</div>
<div class="accordion">Heading<span></span></div>
<div class="container">
	<div class="content">
		<div>Sample Content</div>
		<p>Content here....</p>
	</div>
</div>
<div class="accordion">Heading<span></span></div>
<div class="container">
	<div class="content">
		<div>Sample Content</div>
		<p>Content here....</p>
	</div>
</div>
<div class="accordion">Heading<span></span></div>
<div class="container">
	<div class="content">
		<div>Sample Content</div>
		<p>Content here....</p>
	</div>
</div>
<div class="accordion">Heading<span></span></div>
<div class="container">
	<div class="content">
		<div>Sample Content</div>
		<p>Content here....</p>
	</div>
</div>
<div class="accordion">Heading<span></span></div>
<div class="container">
	<div class="content">
		<div>Sample Content</div>
		<p>Content here....</p>
	</div>
</div>
<div class="accordion">Heading<span></span></div>
<div class="container">
	<div class="content">
		<div>Sample Content</div>
		<p>Content here....</p>
	</div>
</div>
<div class="accordion">Heading<span></span></div>
<div class="container">
	<div class="content">
		<div>Sample Content</div>
		<p>Content here....</p>
	</div>
</div>
<div class="accordion">Heading<span></span></div>
<div class="container">
	<div class="content">
		<div>Sample Content</div>
		<p>Content here....</p>
	</div>
</div>
<div class="accordion">Heading<span></span></div>
<div class="container">
	<div class="content">
		<div>Sample Content</div>
		<p>Content here....</p>
	</div>
</div>

Summary: (not really)

  • The variables are initialized first and foremost. The values of the variables are set per need basis, along the way.
  • The first each block is to calculate the destination scrollTop values based on the offset.top() positions of all of the accordion elements. Which is then stored in data() property for each accordion element.
  • It does that by first collecting the elements that currently have the show class applied to them and stores them in a variable called openedAccordions.
  • Then it temporarily removes this show class from them.
  • Gets the offset.top() position of each element and stores in the elements themselves using the convenient data() method provided by jQuery.
  • Rather than simply re-applying the show class using addClass() method, we actually animate using the slideDown() method and only upon completion of the animation, we apply show using addClass().
  • This is important because a simple direct application of show using addClass() without animation produces an unwanted glitch the first time you, as a user, interact with this whole component. The glitch is basically your first already visible container element that goes away rather instantly, without animation.
  • Reason for the approach above is because when things are animating, browser will return current offset().top position of any given accordion element which may not exactly be what we require i.e. the animation may be in progress and the calculated result coming out of offset().top may be wrong and hence the browser will scroll to an unwanted position. I hope you understand what I am to trying to say here.
  • Moving on ...
  • Upon clicking of any of the accordion elements, some internal variables are defined and their values are set.
  • A typical if clause checking if the element has class named show applied to it.
  • This is the fundamental difference in the approach that you had and the approach I went ahead with. Instead of checking for :visible state, I chose to simply look for a class' existence on an element.
  • The rest of the if clause should be pretty self-explanatory.
  • The last bit is another interesting bit.
  • What is happening is that I am using slideUp to animate the elements so the openedAccordions elements can hide except the one that was just clicked. I hope I am making sense here.
  • So that is about it. Let me know if you have any questions.

Hope this helps.

P.S. I guess the cool bit for me is how it would re-adjust upon fast clicking. Try increasing the scrollDuration and slideDuration values to make all animations go slower and click on random accordion elements to see what I mean by re-adjusting.

Upvotes: 1

lucasnadalutti
lucasnadalutti

Reputation: 5948

Yep, just use the slideUp() and slideDown() methods instead of simply adding the show class.

Updated fiddle here

Upvotes: 1

MiltoxBeyond
MiltoxBeyond

Reputation: 2731

Just made your slide up remove the show class on completion and the slideDown function be used at the end for your container on show.

$(document).ready(function() {
    $('.accordion').click(function(){
        if($(this).next('.container').is(':visible')) {
            var self = $(this);
            $(this).next('.container').slideUp(800,function() {
                self.removeClass('show');
            });
        }
        else {
            $('.accordion').find('.container:visible').slideUp();
            $('.accordion').removeClass('show');
            $(this).addClass('show');
            $(this).next('.container').slideDown();
            $('html, body').animate({
                scrollTop: $(this).offset().top
            }, 200);
            $(this).slideDown();
        }         
    });
});

Here it is running: https://jsfiddle.net/07fdq3t1/6/

Upvotes: 0

Related Questions