utils/dynamicElement.js

import customEvent from './customEvent';

/**
 * The dynamicElement util allows elements to be dynamically added to a common container element,
 * defined in themes\shaw-globalnews\footer.php as <div class="l-dynamicElement"></div>.
 * When a dynamic element is added or removed from the container, an "added" or "removed" event
 * is fired respectively, allowing certain actions to be carried out to the dynaically element
 * through delegation of the 'added' / 'removed' event.
 *
 * Exapmle: In "SocialShare", there's a delegation of the 'added' event, which triggers the setup
 * of the social share button click listeners for dynamically added social share markup.
 *
 * @module dynamicElement
 */
const dynamicElement = {
	selector: '.l-dynamicElement',

	$container: false,

	/*
	 * Add an HTML element to the dynamic container, dispatch an 'added' event when element is added.
	 *
	 * @param {HTMLElement} $elem - element to be added
	 * @param {String} childSelector = optional selector for the element's child element
	 *
	 * @method add
	 */
	add( $elem, childSelector = '' ) {
		this.$container = document.querySelector( this.selector );
		if ( this.$container ) {
			this.$container.appendChild( $elem );

			// When a child selector is provided, watch for the addition
			// of the element identified by childSelector
			if ( childSelector ) {
				const observer = new MutationObserver( ( mutationsList ) => {
					[].forEach.call( mutationsList, ( mutation ) => {
						if ( 'childList' === mutation.type && $elem.querySelector( `${childSelector}:not([data-added])` ) ) {
							$elem.querySelector( `${childSelector}` ).dataset.added = true; // eslint-disable-line no-param-reassign
							observer.disconnect();
							customEvent.fire( this.$container, 'added' );
						}
					});
				});

				// Start observing the target node for configured mutations
				observer.observe( $elem, {
					attributes: false,
					childList: true,
					subtree: true,
				});
			} else {
				customEvent.fire( this.$container, 'added' );
			}
		}
	},

	/*
	 * Remove an HTML element to the dynamic container,
	 * dispatch an 'removed' event when element is removed.
	 *
	 * @param {HTMLElement} $elem - element to be removed
	 *
	 * @method remove
	 */
	remove( $elem ) {
		if ( this.$container ) {
			$elem.parentNode.removeChild( $elem ); // IE11 doesn't support remove()
			customEvent.fire( this.$container, 'removed' );
		}
	},
};

export default dynamicElement;