DANIEL DANIEL - 1 month ago 10
jQuery Question

Multi-level Accordion Menu: Its possible to open each level with an anchor?

I have a Multi-Level Accordion Menu - from a FAQ - with 2 levels.

Each level have a 13 Questions / Answers.

My problem:


  • One of the Questions/Answers have a lot of information. (almost 10 pages).

  • The other Questions/Answers have 3, 4 lines of information each.

  • The Question with many information is number 7.



Then When I click on question 7 - the answer 7 is open. with many information, and the user must scroll down the page to read all.

Until here, OK. no problem.

but at end - If I click on Question 8 - to read the answer, the menu closes question 7 (ok) - open question 8 (ok) - but the page still in the footer - the page dont scrolls up with the faq..

With all other questions - because they are small - I can see in same screen almost all 13 questions + the answer opened.

With question 7 - no. the page scrolls down and don't scrolls up - when we close the question 7.

There is a way to solve this? I think I need to create an anchor for each question..

Tks a lot!

https://jsfiddle.net/27sdLvmh/


  • Please, click on Question 7, scrool down to read the answer and then click on question 8. the menu scrolls up - but the screen no.. and the user was lost in the footer of the page.. :D



JS:

;(function ( $, window, document, undefined ) {

var pluginName = 'accordion',
defaults = {
transitionSpeed: 300,
transitionEasing: 'ease',
controlElement: '[data-control]',
contentElement: '[data-content]',
groupElement: '[data-accordion-group]',
singleOpen: true
};

function Accordion(element, options) {
this.element = element;
this.options = $.extend({}, defaults, options);
this._defaults = defaults;
this._name = pluginName;
this.init();
}

Accordion.prototype.init = function () {
var self = this,
opts = self.options;

var $accordion = $(self.element),
$controls = $accordion.find('> ' + opts.controlElement),
$content = $accordion.find('> ' + opts.contentElement);

var accordionParentsQty = $accordion.parents('[data-accordion]').length,
accordionHasParent = accordionParentsQty > 0;

var closedCSS = { 'max-height': 0, 'overflow': 'hidden' };

var CSStransitions = supportsTransitions();

function debounce(func, threshold, execAsap) {
var timeout;

return function debounced() {
var obj = this,
args = arguments;

function delayed() {
if (!execAsap) func.apply(obj, args);
timeout = null;
};

if (timeout) clearTimeout(timeout);
else if (execAsap) func.apply(obj, args);

timeout = setTimeout(delayed, threshold || 100);
};
}

function supportsTransitions() {
var b = document.body || document.documentElement,
s = b.style,
p = 'transition';

if (typeof s[p] == 'string') {
return true;
}

var v = ['Moz', 'webkit', 'Webkit', 'Khtml', 'O', 'ms'];

p = 'Transition';

for (var i=0; i<v.length; i++) {
if (typeof s[v[i] + p] == 'string') {
return true;
}
}

return false;
}

function requestAnimFrame(cb) {
if(window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame) {
return requestAnimationFrame(cb) ||
webkitRequestAnimationFrame(cb) ||
mozRequestAnimationFrame(cb);
} else {
return setTimeout(cb, 1000 / 60);
}
}

function toggleTransition($el, remove) {
if(!remove) {
$content.css({
'-webkit-transition': 'max-height ' + opts.transitionSpeed + 'ms ' + opts.transitionEasing,
'transition': 'max-height ' + opts.transitionSpeed + 'ms ' + opts.transitionEasing
});
} else {
$content.css({
'-webkit-transition': '',
'transition': ''
});
}
}

function calculateHeight($el) {
var height = 0;

$el.children().each(function() {
height = height + $(this).outerHeight(true);
});

$el.data('oHeight', height);
}

function updateParentHeight($parentAccordion, $currentAccordion, qty, operation) {
var $content = $parentAccordion.filter('.open').find('> [data-content]'),
$childs = $content.find('[data-accordion].open > [data-content]'),
$matched;

if(!opts.singleOpen) {
$childs = $childs.not($currentAccordion.siblings('[data-accordion].open').find('> [data-content]'));
}

$matched = $content.add($childs);

if($parentAccordion.hasClass('open')) {
$matched.each(function() {
var currentHeight = $(this).data('oHeight');

switch (operation) {
case '+':
$(this).data('oHeight', currentHeight + qty);
break;
case '-':
$(this).data('oHeight', currentHeight - qty);
break;
default:
throw 'updateParentHeight method needs an operation';
}

$(this).css('max-height', $(this).data('oHeight'));
});
}
}

function refreshHeight($accordion) {
if($accordion.hasClass('open')) {
var $content = $accordion.find('> [data-content]'),
$childs = $content.find('[data-accordion].open > [data-content]'),
$matched = $content.add($childs);

calculateHeight($matched);

$matched.css('max-height', $matched.data('oHeight'));
}
}

function closeAccordion($accordion, $content) {
$accordion.trigger('accordion.close');

if(CSStransitions) {
if(accordionHasParent) {
var $parentAccordions = $accordion.parents('[data-accordion]');

updateParentHeight($parentAccordions, $accordion, $content.data('oHeight'), '-');
}

$content.css(closedCSS);

$accordion.removeClass('open');
} else {
$content.css('max-height', $content.data('oHeight'));

$content.animate(closedCSS, opts.transitionSpeed);

$accordion.removeClass('open');
}
}

function openAccordion($accordion, $content) {
$accordion.trigger('accordion.open');
if(CSStransitions) {
toggleTransition($content);

if(accordionHasParent) {
var $parentAccordions = $accordion.parents('[data-accordion]');

updateParentHeight($parentAccordions, $accordion, $content.data('oHeight'), '+');
}

requestAnimFrame(function() {
$content.css('max-height', $content.data('oHeight'));
});

$accordion.addClass('open');
} else {
$content.animate({
'max-height': $content.data('oHeight')
}, opts.transitionSpeed, function() {
$content.css({'max-height': 'none'});
});

$accordion.addClass('open');
}
}

function closeSiblingAccordions($accordion) {
var $accordionGroup = $accordion.closest(opts.groupElement);

var $siblings = $accordion.siblings('[data-accordion]').filter('.open'),
$siblingsChildren = $siblings.find('[data-accordion]').filter('.open');

var $otherAccordions = $siblings.add($siblingsChildren);

$otherAccordions.each(function() {
var $accordion = $(this),
$content = $accordion.find(opts.contentElement);

closeAccordion($accordion, $content);
});

$otherAccordions.removeClass('open');
}

function toggleAccordion() {
var isAccordionGroup = (opts.singleOpen) ? $accordion.parents(opts.groupElement).length > 0 : false;

calculateHeight($content);

if(isAccordionGroup) {
closeSiblingAccordions($accordion);
}

if($accordion.hasClass('open')) {
closeAccordion($accordion, $content);
} else {
openAccordion($accordion, $content);
}
}

function addEventListeners() {
$controls.on('click', toggleAccordion);

$controls.on('accordion.toggle', function() {
if(opts.singleOpen && $controls.length > 1) {
return false;
}

toggleAccordion();
});

$(window).on('resize', debounce(function() {
refreshHeight($accordion);
}));
}

function setup() {
$content.each(function() {
var $curr = $(this);

if($curr.css('max-height') != 0) {
if(!$curr.closest('[data-accordion]').hasClass('open')) {
$curr.css({ 'max-height': 0, 'overflow': 'hidden' });
} else {
toggleTransition($curr);
calculateHeight($curr);

$curr.css('max-height', $curr.data('oHeight'));
}
}
});


if(!$accordion.attr('data-accordion')) {
$accordion.attr('data-accordion', '');
$accordion.find(opts.controlElement).attr('data-control', '');
$accordion.find(opts.contentElement).attr('data-content', '');
}
}

setup();
addEventListeners();
};

$.fn[pluginName] = function ( options ) {
return this.each(function () {
if (!$.data(this, 'plugin_' + pluginName)) {
$.data(this, 'plugin_' + pluginName,
new Accordion( this, options ));
}
});
}

})( jQuery, window, document );

Answer

Hey check this demo

changes line 191 -> 194

setTimeout(function()
{
   $('html, body').animate({ scrollTop: $content.prev().offset().top }, "fast");
},(1000/60)+200);