<header>
	<nav class="navbar">
		<ul class="menu">
			<li class="menu-item">Home</li>
			<li class="menu-item">Services</li>
			<li class="menu-item">Contact</li>
		</ul>
	</nav>
</header>
<div class="container">

<button id="open-modal-1">Open Modal 1</button>
<button id="open-modal-2">Open Modal 2</button>
	
</div>
header {
	align-items: center;
	background-color: #3080B5;
	display: flex;
	height: 75px;
	justify-content: center;
	transition: background 0.3s ease;

	&.modal-open {
		background-color: #ffffff;
	}
}

.menu {
	align-items: center;
	display: flex;
	justify-content: center;
	list-style: none;
}

.menu-item {
	margin-right: 25px;
}

.container {
	align-items: center;
	background: #F7C02D;
	display: flex;
	flex-direction: column;
	height: 100vh;
	justify-content: center;
	width: 100%;
}

.my-modal {
	background: rgba(0,0,0, .5);
	display: none;
	height: 100%;
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	
	&.opened {
		display: block;
	}
	
	&.closed {
		display: none;
	}
}

.my-modal-content {
	padding: 30px;
	position: absolute;
	background-color: #fff;
	height: 40%;
	width: 40%;
	text-align: center;
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	
	h1 {
		margin-top: 0;
	}
}

#open-modal-1,
#open-modal-2 {
	background: #3080B5;
	border: 0;
	border-radius: 3px;
	color: #fff;
	display: block;
	margin-bottom: 30px;
	padding: 20px 30px;
}

#close {
	background: none;
	border: none;
	font-size: 24px;
	position: absolute;
	right: 10px;
	top: 10px;
}

.animate-out {
	animation: out 600ms ease-in-out forwards;
}
  

.animate-in {
	animation: in 500ms ease-in-out forwards;
  display: block;
}
  
@keyframes out {
	0% {
		    transform: translate(-50%, -50%);
	}

	100% {
    transform: translate(-50%, -200%);
		
	}
}
  
  
  
@keyframes in {
	0% {
		opacity: 0;
    	transform: translate(-50%, -200%);
	}
    
	100% {
		opacity: 1;
    	transform: translateX(-50%, -50%);
	}
   
}
  
View Compiled
/**
 * Modal Class
 */
class Modal {

	/**
	 * Constructor
	 *
	 * @param openTrigger The element that will trigger opening the modal.
	 * @param options Options that will override defaults.
	 */
	constructor( openTrigger, options ) {

		/**
		 * Configuration options.
		 *
		 * Merge any user defined options into default config.
		 */
		this.config = Object.assign( {
			backgroundColor: '',
			modalTitle: 'This is a modal!',
			modalText: 'Default description text for modal.',
			onBefore: null,
			onAfter: null
		}, options );

		// Bind callback functions to the Modal.
		this.config.onBefore = this.config.onBefore.bind( this );
		this.config.onAfter = this.config.onAfter.bind( this );

		// Set open trigger.
		this.openTrigger = openTrigger;

		// Set modal events.
		this.bindEvents();
	}

	// Bind events.
	bindEvents() {
		this.openTrigger.addEventListener( 'click', this.open.bind( this ) );
	}

	// Open the modal.
	open() {
		this.render();

		// Cache DOM.
		this.modalDiv = document.getElementById( 'my-modal' );
		this.myModalContent = document.querySelector( '.my-modal-content' );

		// Bind close event.
		this.modalDiv.addEventListener( 'click', this.close.bind( this ) );

		// Call onBefore if it is defined.
		if ( this.config.onBefore ) {
			this.config.onBefore();
		}

		// Add classes.
		this.modalDiv.classList.add( 'opened' );
		this.myModalContent.classList.add( 'animate-in' );

		// Remove animate class.
		setTimeout( () => {
			this.myModalContent.classList.remove( 'animate-in' )
		}, 600 );
	}

	// Close the modal.
	close( e ) {

		// If we click the close button.
		if ( e.target.id === 'close' && e.type === 'click' ) {
			this.myModalContent.classList.add( 'animate-out' );


			// Remove classes.
			setTimeout( () => {

				// Remove classes.
				this.myModalContent.classList.remove( 'animate-out' );
				this.modalDiv.classList.remove( 'opened' );

				// Remove <div> from the DOM.
				this.containerDiv.parentNode.removeChild( this.containerDiv );

				// If onAfter is defined then call it.
				if ( this.config.onAfter ) {
					this.config.onAfter();
				}
			}, 600 );
		}

		return false;
	}

	// Render the modal.
	render() {

		// Set the template.
		const html = this.htmlTemplate();

		// Create a document fragment.
		const docFrag = document.createDocumentFragment();

		// Create a <div> on the fly.
		this.containerDiv = document.createElement( 'div' );

		// Set the HTML of the <div> to the HTML template.
		this.containerDiv.innerHTML = html;

		// Append the modal HTML to the body.
		document.body.appendChild( this.containerDiv );
	}

	// Modal HTML template.
	htmlTemplate() {
		return `
			<div id="my-modal" class="my-modal" style="background-color:${this.config.backgroundColor}";>
				
				<div class="my-modal-content">
					<button id="close">X</button>
					<h1>${this.config.modalTitle}!</h1>
					<p>${this.config.modalText}</p>
				</div>

			</div>
		`;
	}
}

const modalOneTrigger = document.getElementById( 'open-modal-1' );
const modalTwoTrigger = document.getElementById( 'open-modal-2' );
const header = document.getElementsByTagName( 'header' );

new Modal( modalOneTrigger, {
	modalTitle: 'Overriding the Title!',
	onAfter: function() {
		// Let's remove a class to the header!
		header[0].classList.remove( 'modal-open' );
	},
	onBefore: function() {
		// Let's remove a class to the header!
		header[0].classList.add( 'modal-open' );
	}
} );

new Modal( modalTwoTrigger, {
	modalTitle: 'This is modal two!',
	onAfter: function() {
		// Set the background back to the original.
		document.querySelector( '.container' ).style.background = '';
	},
	onBefore: function() {
		// Change the background color of the container!
		document.querySelector( '.container' ).style.background = '#88C542';
	}
} );
View Compiled
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.