main/LoadScript.js

import InView from '../utils/classes/InView';
import customEvent from '../utils/customEvent';

/**
 * Load script defined as attribute `[data-load-script]` as element becomes in view
 *
 * @example
 * <div data-load-script="url" data-placeholder=".c-module__placeholder">
 *     <div class="c-module__placeholder"></div>
 * </div>
 *
 * @module LoadScript
 * @prop {string} selector - targets elements on to set up dynamic loading
 * @prop {string} hiddenCss - CSS class that hides an element
 * @prop {string} loadingCss - CSS class that displays a loading state
 */
const LoadScript = {
	selector: '[data-load-script]:not([data-load-manually])',

	hiddenCss: 'is-hidden',

	loadingCss: 'c-shimmer',

	/**
	 * Set's up `InView` listener on any `[data-load-content]` elements.
	 * Callback is fired when element comes into view.
	 *
	 * @method init
	 */
	init() {
		// set up in view watcher for lazy load ads
		const inViewWatcher = new InView({
			selector: this.selector,
			threshold: 0.1,
			rootMargin: '-100px 0px 0px 0px',
		}, 'scriptWatcher' );
		inViewWatcher.init();

		const $targets = document.querySelectorAll( this.selector );
		[].forEach.call( $targets, ( $target ) => {
			$target.addEventListener( customEvent.IN_VIEW, ( event ) => {
				if ( event && event.detail && event.detail.isInView ) {
					this.load( $target );
				}
			});
		});
	},

	/**
	 * Load script specified by `[data-load-script]`.
	 * Inserts script before first script tag on the page
	 *
	 * @method load
	 */
	load( $target ) {
		const url = $target.dataset.loadScript;
		if ( url ) {
			const tag = document.createElement( 'script' );
			tag.src = url;
			tag.async = true;

			// handle script load
			tag.onload = () => {
				const placeholderSelector = $target.dataset.placeholder;
				if ( placeholderSelector ) {
					const $placeholder = $target.querySelector( placeholderSelector );
					$placeholder.classList.add( this.loadingCss );
					$placeholder.classList.add( this.hiddenCss );
				}
				$target.dataset.scriptLoaded = true; // eslint-disable-line no-param-reassign
				this.broadcastLoad( $target );
			};

			// insert script tag
			const $node = document.getElementsByTagName( 'script' )[0];
			if ( $node && $node.parentNode ) {
				$node.parentNode.insertBefore( tag, $node );
			}
		}
	},

	/**
	 * Fire a custom event signifying script being loaded
	 *
	 * @method broadcastLoad
	 */
	broadcastLoad( $target ) {
		customEvent.fire( window, customEvent.LOADED, {
			target: $target,
		});
	},
};

export default LoadScript;