Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Tabbed description code for Shopify. "Slice" your description with <H6> elements (which will serve as tab titles) and stop with <H5> element.
{% comment %}
if combine_pretext is false, the text before the first <h6> will be shown above all tabs, otherwise added to the first tab
if stop_on_h5 is true, tabs parsing will stop on first H5 element and the rest will be shown below tabs.
What's inside H5 is discarded, but still put something meneningfull there, just in case you switch theme or alike.
No multiple H5 elements.
{% endcomment %}
{% assign combine_pretext = false %}
{% assign stop_on_h5 = true %}
{% assign description = tabbed-description | default: product.description %}
{% if description contains "<h6>" %}
{% assign tab_heads = '' %}
{% assign tab_texts = '' %}
{% assign pretext = '' %}
{% assign posttext = '' %}
{% if stop_on_h5 and description contains "<h5>" %}
{% assign posttext = description | split: "</h5>" | last %}
{% assign description = description | split: "<h5>" | first %}
{% endif %}
{% assign chunks = description | strip | split: "<h6>" %}
{% for c in chunks %}
{% if c contains "</h6>" %}
{% assign chunk = c | split: "</h6>" %}
{% assign tab_heads = tab_heads | append: ",," | append: chunk.first %}
{% assign tab_texts = tab_texts | append: ",," %}
{% if pretext != blank and combine_pretext %}
{% assign tab_texts = tab_texts | append: pretext | append: "<br>" %}
{% assign pretext = '' %}
{% endif %}
{% assign tab_texts = tab_texts | append: chunk.last %}
{% elsif forloop.first %}
{% assign pretext = c %}
{% endif %}
{% endfor %}
{% assign tab_heads = tab_heads | remove_first: ",," | split: ",," %}
{% assign tab_texts = tab_texts | remove_first: ",," | split: ",," %}
{% assign index = 1 %}
{% if pretext != blank and combine_pretext == false %}
<div class=pretext>{{ pretext }}</div>
{% endif %}
<div>
<ul class="tabs">
{% for head in tab_heads %}
<li><a href="#tab-{{- index -}}">{{ head }}</a></li>
{% assign index = index | plus: 1 %}
{% endfor %}
</ul>
{% assign index = 1 %}
{% for text in tab_texts %}
<div id="tab-{{- index -}}">{{ text }}</div>
{% assign index = index | plus: 1 %}
{% endfor %}
</div>
{% if posttext != blank %}
<div class=posttext>{{ posttext }}</div>
{% endif %}
<script>
document.addEventListener( 'DOMContentLoaded', function () {
function findTabContent( link ){
if( link && link.nodeName == "A" ){
var contentID = link.attributes.href.value.replace('#','');
return document.getElementById( contentID );
}
else
return null;
}
function hide( element ){
if( element )
element.style.display="none";
}
function show( element ){
if( element )
element.style.display="";
}
function setActive( element ){
if( element ){
element.classList.add('active');
var c = findTabContent( element );
show(c);
}
}
var tabHeads = document.querySelectorAll('.tabs');
tabHeads.forEach( function( tablist ){
var links = document.querySelectorAll('.tabs a'),
tabs = document.querySelectorAll('.tabs li');
if( links.length == 0 )
return;
var active = links[0],
content = findTabContent( active ),
totalWidth = 0, currentMode;
setActive( active );
links.forEach( function( el, index ){
if( index > 0 )
hide( findTabContent( el ) );
});
/* event listener for clicking tabs */
tablist.addEventListener( 'click', function(e){
var clicked = e.target;
if( clicked.nodeName !== "A" )
return;
e.preventDefault();
active.classList.remove('active');
hide( content );
active = clicked;
content = findTabContent( clicked );
setActive( clicked );
return false;
});
function onResize( e ){
if( e.type == 'load' ){
/* how wide are all our tab headers together? calculate this once, on load */
tabs.forEach( function( el, i ) {
totalWidth += el.offsetWidth ;
});
}
/* will they fit on one line? check on every resize/rotate */
if( tablist.clientWidth > totalWidth ){
if( currentMode !== 'wide' ) {
tablist.classList.remove('vertical');
currentMode = 'wide';
}
} else{
if( currentMode !== 'narrow' ) {
tablist.classList.add('vertical');
currentMode = 'narrow';
}
}
}
window.addEventListener( 'resize', onResize );
window.addEventListener( 'load', onResize );
});
});
</script>
<style>
ul.tabs {
border-bottom: 1px solid #DDDDDD;
display: block;
margin: 0 0 20px;
padding: 0;
}
ul.tabs li {
display: block;
float: left;
height: 30px;
margin-bottom: 0;
padding: 0;
width: auto;
}
ul.tabs li a {
-moz-border-bottom-colors: none;
-moz-border-image: none;
-moz-border-left-colors: none;
-moz-border-right-colors: none;
-moz-border-top-colors: none;
background: none repeat scroll 0 0 #F5F5F5;
border-color: #DDDDDD !important;
border-style: solid;
border-width: 1px 1px 0 1px;
display: block;
font-size: 13px;
height: 29px;
line-height: 30px;
margin: 0;
padding: 0 20px;
text-decoration: none;
width: auto;
color: #303030;
border-bottom:none !important;
}
ul.tabs li a.active {
background: none repeat scroll 0 0 #FFFFFF;
border-left-width: 1px;
border-top-left-radius: 2px;
border-top-right-radius: 2px;
color: #111111;
height: 30px;
margin: 0 0 0 -1px;
padding-top: 4px;
position: relative;
top: -4px;
}
ul.tabs li:first-child a.active {
margin-left: 0;
}
ul.tabs li:first-child a {
border-top-left-radius: 2px;
border-width: 1px 1px 0;
}
ul.tabs li:last-child a {
border-top-right-radius: 2px;
}
ul.tabs:before, ul.tabs:after {
content: " ";
display: block;
height: 0;
overflow: hidden;
visibility: hidden;
width: 0;
}
ul.tabs:after {
clear: both;
}
/* update for better mobile look */
ul.tabs.vertical> li {
width: 100%;
}
</style>
{% else %}
{{ description }}
{% endif %}
@OkayDokayOkay

This comment has been minimized.

Copy link

@OkayDokayOkay OkayDokayOkay commented May 18, 2020

Hello,
I am using this code for my shop - works great.
However, when I am changing the styling, e.g. background color when tab active from #FFFFFF to #ff0266 for example, nothing changes.
It seems like something is overwriting my styling - how can I fix this?
Best,
OKAY

@davidkensell

This comment has been minimized.

Copy link

@davidkensell davidkensell commented Nov 15, 2020

Would be amazing to have a Vanilla JS version of this. Shopify's putting a lot of emphasis on page speed, jQuery has fallen out of favor.

@tairli

This comment has been minimized.

Copy link
Owner Author

@tairli tairli commented Jan 18, 2021

Vanilla JS version is here :)

@tairli

This comment has been minimized.

Copy link
Owner Author

@tairli tairli commented Jan 24, 2021

Now you can "stop" tabs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment