$content.attr({'role': 'tabpanel', 'aria-labelledby': linkId, 'aria-hidden': true, 'id': id});
});
+
var $initActive = this.$element.find('.is-active').children('[data-tab-content]');
- this.firstTimeInit = true;
- if($initActive.length){
- this.down($initActive, this.firstTimeInit);
- this.firstTimeInit = false;
+ if ($initActive.length) {
+ // Save up the initial hash to return to it later when going back in history
+ this._initialAnchor = $initActive.prev('a').attr('href');
+ this._openSingleTab($initActive);
}
this._checkDeepLink = () => {
var anchor = window.location.hash;
- //need a hash and a relevant anchor in this tabset
- if(anchor.length) {
- var $link = this.$element.find('[href$="'+anchor+'"]'),
- $anchor = $(anchor);
-
- if ($link.length && $anchor) {
- if (!$link.parent('[data-accordion-item]').hasClass('is-active')) {
- this.down($anchor, this.firstTimeInit);
- this.firstTimeInit = false;
- };
-
- //roll up a little to show the titles
- if (this.options.deepLinkSmudge) {
- var _this = this;
- onLoad($(window), function() {
- var offset = _this.$element.offset();
- $('html, body').animate({ scrollTop: offset.top }, _this.options.deepLinkSmudgeDelay);
- });
- }
-
- /**
- * Fires when the zplugin has deeplinked at pageload
- * @event Accordion#deeplink
- */
- this.$element.trigger('deeplink.zf.accordion', [$link, $anchor]);
- }
+
+ // If there is no anchor, return to the initial panel
+ if (!anchor.length && this._initialAnchor) {
+ anchor = this._initialAnchor;
+ }
+
+ var $anchor = anchor && $(anchor);
+ var $link = anchor && this.$element.find(`[href$="${anchor}"]`);
+
+ // If there is an anchor for the hash, open it (if not already active)
+ if ($anchor && $link && $link.length) {
+ if (!$link.parent('[data-accordion-item]').hasClass('is-active')) {
+ this._openSingleTab($anchor);
+ };
+ }
+ // Otherwise, close everything
+ else {
+ this._closeAllTabs();
+ }
+
+ // Roll up a little to show the titles
+ if (this.options.deepLinkSmudge) {
+ onLoad($(window), () => {
+ var offset = this.$element.offset();
+ $('html, body').animate({ scrollTop: offset.top }, this.options.deepLinkSmudgeDelay);
+ });
+ }
+
+ if ($anchor && $link) {
+ /**
+ * Fires when the plugin has deeplinked at pageload
+ * @event Accordion#deeplink
+ */
+ this.$element.trigger('deeplink.zf.accordion', [$link, $anchor]);
}
}
/**
* Opens the accordion tab defined by `$target`.
* @param {jQuery} $target - Accordion pane to open (`.accordion-content`).
- * @param {Boolean} firstTime - flag to determine if reflow should happen.
* @fires Accordion#down
* @function
*/
- down($target, firstTime) {
- /**
- * checking firstTime allows for initial render of the accordion
- * to render preset is-active panes.
- */
- if ($target.closest('[data-accordion]').is('[disabled]') && !firstTime) {
+ down($target) {
+ if ($target.closest('[data-accordion]').is('[disabled]')) {
console.info('Cannot call down on an accordion that is disabled.');
return;
}
- $target
- .attr('aria-hidden', false)
- .parent('[data-tab-content]')
- .addBack()
- .parent().addClass('is-active');
-
- if (!this.options.multiExpand && !firstTime) {
- var $currentActive = this.$element.children('.is-active').children('[data-tab-content]');
- if ($currentActive.length) {
- this.up($currentActive.not($target));
- }
+
+ if (this.options.multiExpand)
+ this._openTab($target);
+ else
+ this._openSingleTab($target);
+ }
+
+ /**
+ * Closes the tab defined by `$target`.
+ * It may be ignored if the Accordion options don't allow it.
+ *
+ * @param {jQuery} $target - Accordion tab to close (`.accordion-content`).
+ * @fires Accordion#up
+ * @function
+ */
+ up($target) {
+ if (this.$element.is('[disabled]')) {
+ console.info('Cannot call up on an accordion that is disabled.');
+ return;
+ }
+
+ // Don't close the item if it is already closed
+ const $targetItem = $target.parent();
+ if (!$targetItem.hasClass('is-active')) return;
+
+ // Don't close the item if there is no other active item (unless with `allowAllClosed`)
+ const $othersItems = $targetItem.siblings();
+ if (!this.options.allowAllClosed && !$othersItems.hasClass('is-active')) return;
+
+ this._closeTab($target);
+ }
+
+ /**
+ * Make the tab defined by `$target` the only opened tab, closing all others tabs.
+ * @param {jQuery} $target - Accordion tab to open (`.accordion-content`).
+ * @function
+ * @private
+ */
+ _openSingleTab($target) {
+ // Close all the others active tabs.
+ const $activeContents = this.$element.children('.is-active').children('[data-tab-content]');
+ if ($activeContents.length) {
+ this._closeTab($activeContents.not($target));
}
+ // Then open the target.
+ this._openTab($target);
+ }
+
+ /**
+ * Opens the tab defined by `$target`.
+ * @param {jQuery} $target - Accordion tab to open (`.accordion-content`).
+ * @fires Accordion#down
+ * @function
+ * @private
+ */
+ _openTab($target) {
+ const $targetItem = $target.parent();
+ const targetContentId = $target.attr('aria-labelledby');
+
+ $target.attr('aria-hidden', false);
+ $targetItem.addClass('is-active');
+
+ $(`#${targetContentId}`).attr({
+ 'aria-expanded': true,
+ 'aria-selected': true
+ });
+
$target.slideDown(this.options.slideSpeed, () => {
/**
* Fires when the tab is done opening.
*/
this.$element.trigger('down.zf.accordion', [$target]);
});
-
- $(`#${$target.attr('aria-labelledby')}`).attr({
- 'aria-expanded': true,
- 'aria-selected': true
- });
}
/**
* @param {jQuery} $target - Accordion tab to close (`.accordion-content`).
* @fires Accordion#up
* @function
+ * @private
*/
- up($target) {
- if ($target.closest('[data-accordion]').is('[disabled]')) {
- console.info('Cannot call up on an accordion that is disabled.');
- return;
- }
+ _closeTab($target) {
+ const $targetItem = $target.parent();
+ const targetContentId = $target.attr('aria-labelledby');
- var $aunts = $target.parent().siblings(),
- _this = this;
+ $target.attr('aria-hidden', true)
+ $targetItem.removeClass('is-active');
- if((!this.options.allowAllClosed && !$aunts.hasClass('is-active')) || !$target.parent().hasClass('is-active')) {
- return;
- }
+ $(`#${targetContentId}`).attr({
+ 'aria-expanded': false,
+ 'aria-selected': false
+ });
- $target.slideUp(_this.options.slideSpeed, function () {
+ $target.slideUp(this.options.slideSpeed, () => {
/**
* Fires when the tab is done collapsing up.
* @event Accordion#up
*/
- _this.$element.trigger('up.zf.accordion', [$target]);
+ this.$element.trigger('up.zf.accordion', [$target]);
});
+ }
- $target.attr('aria-hidden', true)
- .parent().removeClass('is-active');
-
- $(`#${$target.attr('aria-labelledby')}`).attr({
- 'aria-expanded': false,
- 'aria-selected': false
- });
+ /**
+ * Closes all active tabs
+ * @fires Accordion#up
+ * @function
+ * @private
+ */
+ _closeAllTabs() {
+ var $activeTabs = this.$element.children('.is-active').children('[data-tab-content]');
+ if ($activeTabs.length) {
+ this._closeTab($activeTabs);
+ }
}
/**
'aria-labelledby': linkId
});
+ // Save up the initial hash to return to it later when going back in history
+ if (isActive) {
+ _this._initialAnchor = `#${hash}`;
+ }
+
if(!isActive) {
$tabContent.attr('aria-hidden', 'true');
}
});
}
});
+
if(this.options.matchHeight) {
var $images = this.$tabContent.find('img');
}
}
- //current context-bound function to open tabs on page load or history hashchange
+ // Current context-bound function to open tabs on page load or history hashchange
this._checkDeepLink = () => {
var anchor = window.location.hash;
- //need a hash and a relevant anchor in this tabset
- if(anchor.length) {
- var $link = this.$element.find('[href$="'+anchor+'"]');
- if ($link.length) {
- this.selectTab($(anchor), true);
-
- //roll up a little to show the titles
- if (this.options.deepLinkSmudge) {
- var offset = this.$element.offset();
- $('html, body').animate({ scrollTop: offset.top }, this.options.deepLinkSmudgeDelay);
- }
- /**
- * Fires when the zplugin has deeplinked at pageload
- * @event Tabs#deeplink
- */
- this.$element.trigger('deeplink.zf.tabs', [$link, $(anchor)]);
- }
- }
- }
+ // If there is no anchor, return to the initial panel
+ if (!anchor.length && this._initialAnchor) {
+ anchor = this._initialAnchor;
+ }
+
+ var $anchor = anchor && $(anchor);
+ var $link = anchor && this.$element.find('[href$="'+anchor+'"]');
+
+ // If there is an anchor for the hash, select it
+ if ($anchor && $anchor.length && $link && $link.length) {
+ this.selectTab($anchor, true);
+ }
+ // Otherwise, collapse everything
+ else {
+ this._collapse();
+ }
+
+ // Roll up a little to show the titles
+ if (this.options.deepLinkSmudge) {
+ var offset = this.$element.offset();
+ $('html, body').animate({ scrollTop: offset.top }, this.options.deepLinkSmudgeDelay);
+ }
+
+ if ($anchor && $link) {
+ /**
+ * Fires when the plugin has deeplinked at pageload
+ * @event Tabs#deeplink
+ */
+ this.$element.trigger('deeplink.zf.tabs', [$link, $anchor]);
+ }
+ }
//use browser to open a tab, if it exists in this tabset
if (this.options.deepLink) {
*/
_handleTabChange($target, historyHandled) {
- /**
- * Check for active class on target. Collapse if exists.
- */
+ // With `activeCollapse`, if the target is the active Tab, collapse it.
if ($target.hasClass(`${this.options.linkActiveClass}`)) {
if(this.options.activeCollapse) {
- this._collapseTab($target);
-
- /**
- * Fires when the zplugin has successfully collapsed tabs.
- * @event Tabs#collapse
- */
- this.$element.trigger('collapse.zf.tabs', [$target]);
+ this._collapse();
}
return;
}
.attr({ 'aria-hidden': 'true' })
}
+ /**
+ * Collapses the active Tab.
+ * @fires Tabs#collapse
+ * @function
+ */
+ _collapse() {
+ var $activeTab = this.$element.find(`.${this.options.linkClass}.${this.options.linkActiveClass}`);
+
+ if ($activeTab.length) {
+ this._collapseTab($activeTab);
+
+ /**
+ * Fires when the plugin has successfully collapsed tabs.
+ * @event Tabs#collapse
+ */
+ this.$element.trigger('collapse.zf.tabs', [$activeTab]);
+ }
+ }
+
/**
* Public method for selecting a content pane to display.
* @param {jQuery | String} elem - jQuery object or string of the id of the pane to display.
this._handleTabChange($target, historyHandled);
};
+
/**
* Sets the height of each panel to the height of the tallest panel.
* If enabled in options, gets called on media query change.