Skip to content

Instantly share code, notes, and snippets.

@stewartknapman
Last active June 13, 2019 14:37
Show Gist options
  • Save stewartknapman/6596812a4122ec92d686f943c9bcf2a2 to your computer and use it in GitHub Desktop.
Save stewartknapman/6596812a4122ec92d686f943c9bcf2a2 to your computer and use it in GitHub Desktop.
Tabs for use with Debut
<div class="description-tabs" data-tabs>
<!-- Tab header and content. Repeat for each tab. -->
<div class="description-tab__header">
<h4>
<a href="#tab1" data-tab-target="#tab1" data-tab-active> <!-- Only add data-tab-active to the one active tab you want to show first -->
<span>Tab 1 title</span>
</a>
</h4>
</div>
<div class="description-tab__content-wrapper" id="tab1">
<div class="description-tab__content">
Tab 1 content
</div>
</div>
<!-- end tab header and content -->
</div>
(function () {
if (!document.querySelector && !window.addEventListener && !("classList" in document.createElement("p"))) return;
var ready = function (callback, ctx) {
if (typeof callback !== 'function') return;
if (document.readyState !== "loading") {
callback.apply(ctx);
} else {
document.addEventListener("DOMContentLoaded", function () {
callback.apply(ctx);
});
}
};
var each = function (arr, callback, ctx) {
var r;
for (var i = 0; i < arr.length; i++) {
ctx = ctx || arr[i];
r = callback.apply(ctx, [arr[i], i]);
if (r == false) break;
}
};
/* Description Tab */
var Tabs = function (ele) {
this.ele = ele;
this.selectors = {
actions: '[data-tab-target]',
active: '[data-tab-active]'
};
this.classes = {
hasTabs: 'has-tabs'
};
this.attributes = {
active: 'data-tab-active',
target: 'data-tab-target'
};
this.hasHistoryState = false;
this.allowOpenTabToBeClosed = false; // Like an accordion
};
Tabs.prototype.init = function () {
if (!this.ele) return;
this.actions = this.ele.querySelectorAll(this.selectors.actions);
if (this.actions.length) {
var active = this.ele.querySelector(this.selectors.active) || this.actions[0];
document.documentElement.classList.add(this.classes.hasTabs);
this._addEventListeners();
this.closeAllTabs();
this.openTab(active);
this.scrollToActive();
}
};
Tabs.prototype.openTab = function (ele) {
this.active = ele;
ele.setAttribute(this.attributes.active, '');
var targetStr = ele.getAttribute(this.attributes.target);
var targets = this.ele.querySelectorAll(targetStr);
each(targets, function (target) {
target.style.display = 'block';
});
};
Tabs.prototype.closeTab = function (ele) {
ele.removeAttribute(this.attributes.active);
var targetStr = ele.getAttribute(this.attributes.target);
var targets = this.ele.querySelectorAll(targetStr);
each(targets, function (target) {
target.style.display = 'none';
});
};
Tabs.prototype.closeAllTabs = function (currentTarget) {
var target,
targetStr,
_this = this;
this._eachAction(function (tabAction) {
if (_this.allowOpenTabToBeClosed) {
_this.closeTab(tabAction);
} else if (currentTarget != tabAction) {
_this.closeTab(tabAction);
}
});
if (this.allowOpenTabToBeClosed) {
this.active = false;
}
};
Tabs.prototype.scrollToActive = function () {
if (!!location.hash) {
active = this.ele.querySelector('[href="'+location.hash+'"]');
}
};
Tabs.prototype._updateHash = function (hash) {
if (history.pushState) {
history.pushState(null, null, hash);
} else {
location.hash = hash;
}
};
Tabs.prototype._addEventListeners = function () {
var _this = this;
this._eachAction(function (tabAction) {
tabAction.addEventListener('click', function (e) {
e.preventDefault();
_this._onClick(e.currentTarget);
});
});
};
Tabs.prototype._onClick = function (currentTarget) {
var lastActive = this.active;
this.closeAllTabs(currentTarget);
if (currentTarget !== lastActive) {
// only open the tab if it wasn't already open
this.openTab(currentTarget);
}
if (this.hasHistoryState) {
this._updateHash(currentTarget.getAttribute('href'));
}
currentTarget.blur();
};
Tabs.prototype._eachAction = function (callback) {
for (var i = 0; i < this.actions.length; i++) {
callback(this.actions[i]);
}
};
/* Description Tabs */
var TabGroups = function () {
this.selectors = {
ele: '[data-tabs]'
};
this.init();
};
TabGroups.prototype.init = function () {
var tab,
_this = this;
this.eles = document.querySelectorAll(this.selectors.ele);
each(this.eles, function (ele) {
tab = new Tabs(ele);
tab.init();
});
}
// Run everything
ready(function () {
new TabGroups();
});
})();
.description-tab__header {
padding-top: 1.5em;
.no-js &:first-child {
padding-top: 0;
}
}
.has-tabs {
.description-tabs {
position: relative;
@include display-flexbox();
@include flex-direction(row);
@include flex-wrap(wrap);
margin: 1.5em 0;
}
.description-tab__header {
@include flex(1 1 100%);
width: 100%;
padding: 0;
@media (min-width: 60em) {
@include flex(1 1 6em);
order: 0;
width: auto;
text-align: center;
&:first-child {
text-align: left;
}
&:nth-last-child(2) {
text-align: right;
}
}
h4 {
height: 100%;
margin-bottom: 0;
font-size: 1.25em;
}
a {
display: block;
height: 100%;
padding: 0.5em 0;
border-bottom: none;
color: $color-body-text--light;
&:hover,
&[data-tab-active] {
color: $color-body-text;
}
}
a > span {
display: block;
position: relative;
top: 50%;
-moz-transform: translateY(-50%);
-ms-transform: translateY(-50%);
transform: translateY(-50%);
}
}
.description-tab__content-wrapper {
width: 100%;
padding-bottom: 1.5em;
@media (min-width: 60em) {
order: 1;
padding-bottom: 0;
}
}
.description-tab__content {
@extend .rte;
max-width: 40em;
}
.description-tab__content--wide {
max-width: none;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment