Skip to content

Instantly share code, notes, and snippets.

@elbakerino
Last active June 14, 2022 16:31
Show Gist options
  • Save elbakerino/99484402c18035d6b8594d006500a0d0 to your computer and use it in GitHub Desktop.
Save elbakerino/99484402c18035d6b8594d006500a0d0 to your computer and use it in GitHub Desktop.
collapse.js
/*
* Simple vanilla JS height collapsible.
* For HTML with a [data-toggle] and a `.collapse` as next neighbor:,
* License: by github.com/elbakerino 2022, WTFPL https://en.wikipedia.org/wiki/WTFPL
* <ul>
* <li>
* <button data-toggle="on">Toggle 1</button>
* <div class="collapse closed">
* <div>
* <h2>Item 1</h2>
* <p>Lorem ipsum dolor sit amet consectutor adispisci.</p>
* <a href="/">More</a>
* </div>
* </div>
* </li>
* </ul>
* Example Style:
* .collapse {
* overflow: hidden;
* transition: height 0.3625s ease-out;
* }
* .collapse.closed {
* height: 0;
* }
*/
const collapse = (toggles) => {
const unsub = toggles.map((node) => {
const handleToggle = (target) => {
const nextSibling = target.nextElementSibling;
if(!nextSibling) return;
if(nextSibling.classList.contains('closed')) {
if(nextSibling.style.height !== '0') {
nextSibling.style.height = '0';
}
nextSibling.classList.remove('closed');
nextSibling.style.height = nextSibling.scrollHeight + 'px';
} else {
nextSibling.classList.add('closed');
nextSibling.style.height = '0';
}
};
const onClick = (e) => {
handleToggle(e.target);
};
const onKeyPress = (e) => {
if(e.key === 'Enter') {
e.preventDefault();
e.stopPropagation();
handleToggle(e.target);
}
};
node.addEventListener('click', onClick);
node.addEventListener('keypress', onKeyPress);
return () => {
node.removeEventListener('click', onClick);
node.removeEventListener('keypress', onKeyPress);
};
});
const onResize = () => {
toggles.forEach((toggle) => {
const nextSibling = toggle.nextElementSibling;
if(!nextSibling) return;
if(!nextSibling.classList.contains('closed')) {
nextSibling.style.height = nextSibling.scrollHeight + 'px';
}
});
};
window.addEventListener('resize', onResize);
return () => {
unsub.forEach(u => u());
window.removeEventListener('resize', onResize);
};
};
// select and bind the collapse toggles
const unsub = collapse(
Array.from(document.querySelectorAll('[data-toggle]'))
);
// when using in e.g. stateful UI, remember to unsub on e.g. unmount:
// unsub()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment