Dom navigation for custom jQuery accordion setup
I have an accordion working and it works really well when you go down the accordions, but when you come back it looks like a buggy one.
I want to do this, when you click on a mast, it will open the content inside that mast and anchor the top of the page to the top of the mast. I know what I need to do in pseudocode, but I'm not sure what to actually do with the code.
Here's my HTML:
<html>
<head>
<meta>
<title></title>
<link rel="stylesheet" href="made-insider.css">
</head>
<body>
<div class="accordion">
<div id="one" class="masthead"></div>
<div class="insider-info"></div>
<div id="two" class="masthead"></div>
<div class="insider-info"></div>
<div id="three" class="masthead"></div>
<div class="insider-info"></div>
<div id="four" class="masthead"></div>
<div class="insider-info"></div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="made-insider.js"></script>
</body>
</html>
Here's my jQuery:
$(function() {
//Checking the position of panels
var allPanels = $('.insider-info').hide();
//working accordion code -- needs to be smoother
$('.accordion > .masthead').click(function(event) {
if ($(this).hasClass('active')) {
$(this).removeClass('active')
.next(allPanels).slideUp(400);
} else {
var topPos = $(this).position();
$('.active').removeClass('active')
.next(allPanels).slideUp(400);
//if the previous accordion is open
$('body').animate({ scrollTop: topPos.top - 200}, 600);
//if the previous accordion is not open
//$('body').animate({ scrollTop: topPos.top}, 600);
$(this).addClass('active')
.next(allPanels).slideDown(400);
}
});
});
I have tried things like
if ($(this).prev('.masthead').hasClass('.active')){
behavior
},
if ($(this).prev().prev().hasClass('.active'){
behavior
}
if ($(this).eq() < $('div.active').eq()){
behavior
}
but none of them work. Any suggestions?
source to share
The problem is that the place in your code where you need to know the index .active
is triggered after you remove the class .active
from any of its elements.
Solution: move this bit of code
$('.active').removeClass('active')
.next(allPanels).slideUp(400);
to the end of the event handler, before adding the class .active
to the new active element.
Then the condition you are looking for is
if ($('.active').length && $('.active').index() < $(this).index()) {
// the previously active accordion is above the new one (we're moving down)
}
So, put together, you will look like this:
$('.accordion > .masthead').click(function (event) {
if ($(this).hasClass('active')) {
$(this).removeClass('active')
.next(allPanels).slideUp(400);
} else {
var topPos = $(this).position();
// the previously active accordion is above the new one
if ($('.active').length && $('.active').index() < $(this).index()) {
$('body').animate({
scrollTop: topPos.top - 200
}, 600);
} else { // the previously active accordion is below the new one, or there was no previous accordion
$('body').animate({
scrollTop: topPos.top
}, 600);
}
$('.active').removeClass('active')
.next(allPanels).slideUp(400);
$(this).addClass('active')
.next(allPanels).slideDown(400);
}
});
source to share
I spent a day this weekend to really clean up the code and improve its functionality.
This code will allow you to create an accordion that is anchored to the top of the tab you click on and is designed to respond to both the size of the mast tab and the content in the tab. Any suggestions on how I could make the code even cleaner would be great!
Check out the code on my GitHub if you like: https://github.com/realjoet/smooth-flexible-accordion
$(function () {
//Checking the position of panels
var allContentPanels = $('.content');
var allMastheads = $('.masthead');
var hideContentPanels = allContentPanels.hide();
//If you want margin on your mastheads, enter it here just like CSS
var mastheadMargin = "0";
//Need to fill these in with negative values of the values you put in for mastheadMargin
var marginTopFix = "0";
var marginRightFix = "0";
var marginBottomFix = "0";
var marginLeftFix = "0";
allMastheads.css("margin", mastheadMargin);
//working accordion code
$('.accordion > .masthead').click(function() {
var el = $(this);
var activeEl = $('.active');
var contentHeight = $('.active').next().height();
var topPos = $(this).position().top;
function marginFix(el) {
$(el).next().css({
"position": "relative",
"top": marginTopFix,
"right": marginRightFix,
"bottom": marginBottomFix,
"left": marginLeftFix
}).delay(10);
}
//Accordion function
function accordionFunc(el, animationTime, animationTime2) {
if (el.hasClass('active')) {
el.removeClass('active')
.next(allContentPanels).slideUp(animationTime);
$('body').animate({scrollTop: topPos}, animationTime);
} else {
if (activeEl.length && activeEl.index() < el.index()) {
$('body').animate({scrollTop: topPos - contentHeight}, animationTime2);
} else {
$('body').animate({scrollTop: topPos}, animationTime2);
}
activeEl.removeClass('active')
.next(allContentPanels).slideUp(animationTime);
el.addClass('active')
.next(allContentPanels).slideDown(animationTime);
}
}
//Adjusts masthead margins
if (el.hasClass('active')) {
marginFix(el);
$(allMastheads).css("margin", mastheadMargin);
} else {
marginFix(activeEl);
$(allMastheads).css("margin", mastheadMargin);
el.css("margin", "0");
el.next().css("margin", mastheadMargin);
if (el.next().css("top") == marginTopFix) {
el.next().css("top", "0");
}
}
//Creates content-responsive accordion
if (contentHeight < 400) {
accordionFunc(el, 600, 800);
} else if (contentHeight < 800) {
accordionFunc(el, 800, 1000);
} else if (contentHeight < 1200) {
accordionFunc(el, 1000, 1200);
} else {
accordionFunc(el, 1200, 1400);
}
});
});
source to share