/**
* Reveals and hides elements on the page via a toggle. Expandable elements are initially hidden.
*
* @example
* <button data-expand="#regionExpand" aria-expaneded="false">Click to reveal element below</button>
* <div class="is-expandable">Initially hidden content</div>
*
* @module Expand
* @prop {String} expandToggleSelector - selector for all expandable click toggles
* @prop {String} expandedFlag - css class to add once an element is expanded
* @prop {String} scrollParentSelector - default scroll target to body element
*/
const Expand = {
expandToggleSelector: '[data-expand]',
expandedFlag: 'is-expanded',
scrollTargetSelector: 'body',
/**
* Binds click events to any `data-expand="[selector]"` elements on the page.
* @method init
*/
init() {
const $expandToggles = document.querySelectorAll( this.expandToggleSelector );
[].forEach.call( $expandToggles, ( $toggle ) => {
$toggle.addEventListener( 'click', ( e ) => {
e.preventDefault();
const targetId = $toggle.getAttribute( 'data-expand' );
const $target = document.querySelector( targetId );
const $child = $target.children[0];
if ( $target.classList.contains( this.expandedFlag ) ) {
$target.style.height = '';
$target.classList.remove( this.expandedFlag );
$toggle.setAttribute( 'aria-expanded', false );
} else {
$target.style.height = `${$child.offsetHeight}px`;
$target.classList.add( this.expandedFlag );
$toggle.setAttribute( 'aria-expanded', true );
// adjust scroll position to fit expanded element
setTimeout( () => this.setScrollPosition( $toggle, $child ), 500 );
}
});
});
},
/**
* Adjusts scroll position so expanded element is in view
* @method setScrollPosition
* @param {Element} $toggle - DOM reference to the clicked toggle element
* @param {Element} $childe - DOM reference to the exapanding element
*/
setScrollPosition( $toggle, $child ) {
// check to see if user has defined a scroll target, otherwise adjust body scrollbar
if ( $toggle.getAttribute( 'data-scroll-target' ) ) {
this.scrollTargetSelector = $toggle.getAttribute( 'data-scroll-target' );
}
// scroll offset calculation
const $scrollTarget = document.querySelector( this.scrollTargetSelector );
const { scrollTop } = $scrollTarget;
const viewportHeight = window.innerHeight;
const elemBounds = $child.getBoundingClientRect();
const elemVisible = viewportHeight - ( elemBounds.top + scrollTop );
const elemHidden = elemBounds.height - elemVisible;
const scrollPadding = 40;
const scrollOffset = elemHidden + scrollPadding;
// scroll adjustment
$scrollTarget.scrollTop = scrollOffset;
},
};
export default Expand;