Skip to content

Instantly share code, notes, and snippets.

@superfein
Forked from tairli/tabbed-description.liquid
Created February 13, 2023 07:31
Show Gist options
  • Save superfein/65f179d3222c5b6e0f650b1855e2c547 to your computer and use it in GitHub Desktop.
Save superfein/65f179d3222c5b6e0f650b1855e2c547 to your computer and use it in GitHub Desktop.
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 | replace: ' data-mce-fragment="1"', '' %}
{% 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 %}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment