Last active
December 21, 2021 06:51
-
-
Save iamrobert/d612fa701588ab477c0b168b7ecf81db to your computer and use it in GitHub Desktop.
animate details/summary (Maybe this: https://codepen.io/giana/pen/OrpdLK)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Accordion { | |
constructor(el) { | |
// Store the <details> element | |
this.el = el; | |
// Store the <summary> element | |
this.summary = el.querySelector('summary'); | |
// Store the <div class="content"> element | |
this.content = el.querySelector('.p-all'); | |
// Store the animation object (so we can cancel it if needed) | |
this.animation = null; | |
// Store if the element is closing | |
this.isClosing = false; | |
// Store if the element is expanding | |
this.isExpanding = false; | |
// Detect user clicks on the summary element | |
this.summary.addEventListener('click', (e) => this.onClick(e)); | |
} | |
onClick(e) { | |
this.closeAll(); | |
// Stop default behaviour from the browser | |
e.preventDefault(); | |
// Add an overflow on the <details> to avoid content overflowing | |
this.el.style.overflow = 'hidden'; | |
// Check if the element is being closed or is already closed | |
if (this.isClosing || !this.el.open) { | |
this.open(); | |
// Check if the element is being openned or is already open | |
} else if (this.isExpanding || this.el.open) { | |
this.shrink(); | |
} | |
} | |
closeAll(){ | |
let tabs = document.querySelectorAll('details'); | |
for (let index = 0; index < tabs.length; index++) { | |
let summary = tabs[index].querySelector('summary'); | |
const startHeight = `${tabs[index].offsetHeight}px`; | |
const endHeight = `${summary.offsetHeight}px`; | |
let animation = tabs[index].animate({ | |
height: [startHeight, endHeight] | |
}, { | |
duration: 200, | |
easing: 'ease-out' | |
}); | |
animation.onfinish = () => { | |
tabs[index].open = false; | |
tabs[index].style.height = tabs[index].style.overflow = ''; | |
}; | |
} | |
} | |
shrink() { | |
// Set the element as "being closed" | |
this.isClosing = true; | |
// Store the current height of the element | |
const startHeight = `${this.el.offsetHeight}px`; | |
// Calculate the height of the summary | |
const endHeight = `${this.summary.offsetHeight}px`; | |
// If there is already an animation running | |
if (this.animation) { | |
// Cancel the current animation | |
this.animation.cancel(); | |
} | |
// Start a WAAPI animation | |
this.animation = this.el.animate({ | |
// Set the keyframes from the startHeight to endHeight | |
height: [startHeight, endHeight] | |
}, { | |
duration: 200, | |
easing: 'ease-out' | |
}); | |
// When the animation is complete, call onAnimationFinish() | |
this.animation.onfinish = () => this.onAnimationFinish(false); | |
// If the animation is cancelled, isClosing variable is set to false | |
this.animation.oncancel = () => this.isClosing = false; | |
} | |
open() { | |
// Apply a fixed height on the element | |
this.el.style.height = `${this.el.offsetHeight}px`; | |
// Force the [open] attribute on the details element | |
this.el.open = true; | |
// Wait for the next frame to call the expand function | |
window.requestAnimationFrame(() => this.expand()); | |
} | |
expand() { | |
// Set the element as "being expanding" | |
this.isExpanding = true; | |
// Get the current fixed height of the element | |
const startHeight = `${this.el.offsetHeight}px`; | |
// Calculate the open height of the element (summary height + content height) | |
const endHeight = `${this.summary.offsetHeight + this.content.offsetHeight}px`; | |
// If there is already an animation running | |
if (this.animation) { | |
// Cancel the current animation | |
this.animation.cancel(); | |
} | |
// Start a WAAPI animation | |
this.animation = this.el.animate({ | |
// Set the keyframes from the startHeight to endHeight | |
height: [startHeight, endHeight] | |
}, { | |
duration: 200, | |
easing: 'ease-out' | |
}); | |
// When the animation is complete, call onAnimationFinish() | |
this.animation.onfinish = () => this.onAnimationFinish(true); | |
// If the animation is cancelled, isExpanding variable is set to false | |
this.animation.oncancel = () => this.isExpanding = false; | |
} | |
onAnimationFinish(open) { | |
// Set the open attribute based on the parameter | |
this.el.open = open; | |
// Clear the stored animation | |
this.animation = null; | |
// Reset isClosing & isExpanding | |
this.isClosing = false; | |
this.isExpanding = false; | |
// Remove the overflow hidden and the fixed height | |
this.el.style.height = this.el.style.overflow = ''; | |
} | |
} | |
window.Accordion = Accordion; | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Accordion { | |
constructor(el) { | |
// Store the <details> element | |
this.el = el; | |
// Store the <summary> element | |
this.summary = el.querySelector('summary'); | |
// Store the <div class="content"> element | |
this.content = el.querySelector('.p-all'); | |
// Store the animation object (so we can cancel it if needed) | |
this.animation = null; | |
// Store if the element is closing | |
this.isClosing = false; | |
// Store if the element is expanding | |
this.isExpanding = false; | |
// Detect user clicks on the summary element | |
this.summary.addEventListener('click', (e) => this.onClick(e)); | |
} | |
onClick(e) { | |
// Stop default behaviour from the browser | |
e.preventDefault(); | |
// Add an overflow on the <details> to avoid content overflowing | |
this.el.style.overflow = 'hidden'; | |
// Check if the element is being closed or is already closed | |
if (this.isClosing || !this.el.open) { | |
this.open(); | |
// Check if the element is being openned or is already open | |
} else if (this.isExpanding || this.el.open) { | |
this.shrink(); | |
} | |
} | |
shrink() { | |
// Set the element as "being closed" | |
this.isClosing = true; | |
// Store the current height of the element | |
const startHeight = `${this.el.offsetHeight}px`; | |
// Calculate the height of the summary | |
const endHeight = `${this.summary.offsetHeight}px`; | |
// If there is already an animation running | |
if (this.animation) { | |
// Cancel the current animation | |
this.animation.cancel(); | |
} | |
// Start a WAAPI animation | |
this.animation = this.el.animate({ | |
// Set the keyframes from the startHeight to endHeight | |
height: [startHeight, endHeight] | |
}, { | |
duration: 200, | |
easing: 'ease-out' | |
}); | |
// When the animation is complete, call onAnimationFinish() | |
this.animation.onfinish = () => this.onAnimationFinish(false); | |
// If the animation is cancelled, isClosing variable is set to false | |
this.animation.oncancel = () => this.isClosing = false; | |
} | |
open() { | |
// Apply a fixed height on the element | |
this.el.style.height = `${this.el.offsetHeight}px`; | |
// Force the [open] attribute on the details element | |
this.el.open = true; | |
// Wait for the next frame to call the expand function | |
window.requestAnimationFrame(() => this.expand()); | |
} | |
expand() { | |
// Set the element as "being expanding" | |
this.isExpanding = true; | |
// Get the current fixed height of the element | |
const startHeight = `${this.el.offsetHeight}px`; | |
// Calculate the open height of the element (summary height + content height) | |
const endHeight = `${this.summary.offsetHeight + this.content.offsetHeight}px`; | |
// If there is already an animation running | |
if (this.animation) { | |
// Cancel the current animation | |
this.animation.cancel(); | |
} | |
// Start a WAAPI animation | |
this.animation = this.el.animate({ | |
// Set the keyframes from the startHeight to endHeight | |
height: [startHeight, endHeight] | |
}, { | |
duration: 200, | |
easing: 'ease-out' | |
}); | |
// When the animation is complete, call onAnimationFinish() | |
this.animation.onfinish = () => this.onAnimationFinish(true); | |
// If the animation is cancelled, isExpanding variable is set to false | |
this.animation.oncancel = () => this.isExpanding = false; | |
} | |
onAnimationFinish(open) { | |
// Set the open attribute based on the parameter | |
this.el.open = open; | |
// Clear the stored animation | |
this.animation = null; | |
// Reset isClosing & isExpanding | |
this.isClosing = false; | |
this.isExpanding = false; | |
// Remove the overflow hidden and the fixed height | |
this.el.style.height = this.el.style.overflow = ''; | |
} | |
} | |
window.Accordion = Accordion; | |
/* | |
document.querySelectorAll('details').forEach((el) => { | |
new Accordion(el); | |
}); | |
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<details class="no-cb"> | |
<summary class="no-cb" data-noedit> | |
<h3>I found a stray dog what do I do?</h3> | |
</summary> | |
<div class="p-all" data-noedit> | |
<div class="aa"> | |
<p>Answer Question</p> | |
</div> | |
</div> | |
</details> | |
<details class="no-cb"> | |
<summary class="no-cb" data-noedit> | |
<h3>I want to adopt a dog - what is the adoption process?</h3> | |
</summary> | |
<div class="p-all" data-noedit> | |
<div class="aa"> | |
<p>Answer Question</p> | |
</div> | |
</div> | |
</details> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment