- Add a class to section or widget: zaib-collapsible
- Add a code or html block under it and paste this code:
<!-- ==========================================================
ZAIB Collapsible v1.1
Usage:
1) Paste THIS block once (page or site-wide) in an Elementor HTML widget.
2) Add class "zaib-collapsible" to any container you want collapsible.
3) Optional per-box attributes:
data-collapsed-height="240" (default 200)
data-more="See more" (default "Show more")
data-less="See less" (default "Show less")
Notes:
- Works with multiple instances.
- Button auto-hides if content ≤ collapsed height.
- Fade auto-matches the element’s background.
========================================================== -->
<style>
.zaib-collapsible{
--zaib-collapsed: 200px; /* default; can override via data-collapsed-height or inline var */
--zaib-fade: 72px;
--zaib-bg: #fff; /* set at runtime by JS */
position: relative;
}
.zaib-collapsible.is-limited{
max-height: var(--zaib-collapsed) !important;
overflow: hidden !important;
transition: max-height .35s ease !important;
}
.zaib-collapsible.is-limited::after{
content:"";
position:absolute; left:0; right:0; bottom:0;
height: var(--zaib-fade);
background: linear-gradient(to bottom, rgba(255,255,255,0), var(--zaib-bg));
pointer-events:none;
}
.zaib-collapsible.is-expanded{
max-height:none !important;
overflow:visible !important;
}
.zaib-collapsible.is-expanded::after{ display:none; }
.zaib-collapsible-btn{
display:inline-block;
margin-top:12px;
padding:8px 16px;
background:#222; color:#fff;
border:0; border-radius:6px;
font-size:14px; line-height:1;
cursor:pointer;
transition: background .2s ease;
}
.zaib-collapsible-btn:hover{ background:#000; }
.zaib-collapsible-btn-wrap{ text-align:center; }
</style>
<script>
(function(){
const SEL = '.zaib-collapsible';
function measureAndWire(el){
if(!el || el.dataset.zaibProcessed) return;
// Read options
const collapsed = parseInt(el.getAttribute('data-collapsed-height') || '200', 10);
const labelMore = el.getAttribute('data-more') || 'Show more';
const labelLess = el.getAttribute('data-less') || 'Show less';
// Apply CSS var
el.style.setProperty('--zaib-collapsed', collapsed + 'px');
// Set fade bg to computed background (fallback to #fff)
try{
let bg = getComputedStyle(el).backgroundColor;
if(!bg || bg === 'rgba(0, 0, 0, 0)') {
// climb up until we find a non-transparent bg
let p = el.parentElement;
while(p && (!bg || bg === 'rgba(0, 0, 0, 0)')){
bg = getComputedStyle(p).backgroundColor;
p = p.parentElement;
}
}
el.style.setProperty('--zaib-bg', bg || '#fff');
}catch(e){}
// If short, skip and don't add button
if(el.scrollHeight <= collapsed + 10){
el.dataset.zaibProcessed = '1';
return;
}
// Limit
el.classList.add('is-limited');
// Button
const wrap = document.createElement('div');
wrap.className = 'zaib-collapsible-btn-wrap';
const btn = document.createElement('button');
btn.type = 'button';
btn.className = 'zaib-collapsible-btn';
btn.textContent = labelMore;
// a11y attributes
if(!el.id){ el.id = 'zaibcol-' + Math.random().toString(36).slice(2,9); }
btn.setAttribute('aria-controls', el.id);
btn.setAttribute('aria-expanded', 'false');
wrap.appendChild(btn);
el.insertAdjacentElement('afterend', wrap);
btn.addEventListener('click', function(){
const expanded = el.classList.toggle('is-expanded');
if(expanded){
el.classList.remove('is-limited');
btn.textContent = labelLess;
btn.setAttribute('aria-expanded', 'true');
}else{
el.classList.add('is-limited');
btn.textContent = labelMore;
btn.setAttribute('aria-expanded', 'false');
const r = el.getBoundingClientRect();
if(r.top < 0){ window.scrollBy({ top: r.top - 20, behavior: 'smooth' }); }
}
});
el.dataset.zaibProcessed = '1';
}
function initAll(){
document.querySelectorAll(SEL).forEach(measureAndWire);
}
// Run on DOM ready, window load, and after a small delay (Elementor rendering)
if(document.readyState === 'loading'){
document.addEventListener('DOMContentLoaded', initAll);
} else {
initAll();
}
window.addEventListener('load', function(){ setTimeout(initAll, 50); });
// MutationObserver: handle widgets that render after initial load
const mo = new MutationObserver(function(muts){
for(const m of muts){
if(m.addedNodes && m.addedNodes.length){
// If any added subtree contains a collapsible, init it
if(m.target && (m.target.matches && m.target.matches(SEL) || m.target.querySelector && m.target.querySelector(SEL))){
initAll();
}
m.addedNodes.forEach(n=>{
if(n.nodeType===1 && (n.matches(SEL) || n.querySelector && n.querySelector(SEL))){
initAll();
}
});
}
}
});
mo.observe(document.documentElement, { childList: true, subtree: true });
})();
</script>