Skip to content

Instantly share code, notes, and snippets.

@dannyconnolly
Last active February 4, 2021 19:19
Show Gist options
  • Save dannyconnolly/be1795cfd9054b97d299424d3661b7f4 to your computer and use it in GitHub Desktop.
Save dannyconnolly/be1795cfd9054b97d299424d3661b7f4 to your computer and use it in GitHub Desktop.
Vanilla JS Modal
(function() {
// Define constructor
this.Modal = function() {
// Create global element references
this.closeButton = null;
this.modal = null;
this.overlay = null;
// Determine proper prefix
this.transitionEnd = transitionSelect();
// Define option defaults
var defaults = {
className: 'fade-and-drop',
closeButton: true,
content: "",
maxWidth: 600,
minWidth: 280,
overlay: true,
overlayClose: true
};
// Create options by extending defaults with the passed in arugments
if (arguments[0] && typeof arguments[0] === "object") {
this.options = extendDefaults(defaults, arguments[0]);
}
}
// Public Methods
Modal.prototype.open = function () {
// Build out our Modal
buildOut.call(this);
// Initialize event listeners
initializeEvents.call(this);
/*
* After adding elements to the DOM, use getComputedStyle
* to force the browser to recalc and recognize the elements
* that we just added. This is so that CSS animation has a start point
*/
window.getComputedStyle(this.modal).height;
/*
* Add our open class and check if the modal is taller than the window
* If so, our anchored class is also applied
*/
this.modal.className = this.modal.className + (this.modal.offsetHeight > window.innerHeight ? ' scotch-open scotch-anchored' : ' scotch-open');
this.overlay.className = this.overlay.className + ' scotch-open';
}
Modal.prototype.close = function () {
// store the value of this
var _ = this;
// Remove the open class name
this.modal.className = this.modal.className.replace(' scotch-open', '');
this.overlay.className = this.overlay.className.replace(' scotch-open', '');
/*
* Listen for CSS transitionend event and then
* Remove the nodes from the DOM
*/
this.modal.addEventListener(this.transitionEnd, function(){
_.modal.parentNode.removeChild(_.modal);
});
this.modal.addEventListener(this.transitionEnd, function() {
if(_.overlay.parentNode) _.overlay.parentNode.removeChild(_.overlay);
});
}
// Private Methods
function buildOut() {
var content, contentHolder, docFrag;
/*
* If content is an HTML string, append the HTML string.
* If content is a domNode, append its content.
*/
if (typeof this.options.content === 'string') {
content = this.options.content;
}
else {
content = this.options.content.innerHTML;
}
// Create a document fragment to build with
// Document Fragment is used to construct collections of DOM elements outside of the DOM,
// and is used to cumulatively add what we have built to the DOM.
docFrag = document.createDocumentFragment();
this.modal = document.createElement('div');
this.modal.className = "scotch-modal " + this.options.className;
this.modal.style.minWidth = this.options.minWidth + 'px';
this.modal.style.maxWidth = this.options.maxWidth + 'px';
if (this.options.closeButton === true) {
this.closeButton = document.createElement('button');
this.closeButton.className = 'scotch-close close-button';
this.closeButton.innerHTML = 'x';
this.modal.appendChild(this.closeButton);
}
if (this.options.overlay === true) {
this.overlay = document.createElement('div');
this.overlay.className = 'scotch-overlay ' + this.options.className;
docFrag.appendChild(this.overlay);
}
// Create content holder and append to the modal
contentHolder = document.createElement('div');
contentHolder.className = 'scotch-content';
contentHolder.innerHTML = content;
this.modal.appendChild(contentHolder);
// Append modal to document fragment
docFrag.appendChild(this.modal);
// Append document fragment to body
document.body.appendChild(docFrag);
}
function initializeEvents() {
if (this.closeButton) {
this.closeButton.addEventListener('click', this.close.bind(this));
}
if (this.overlay && this.overlayClose) {
this.overlay.addEventListener('click', this.close.bind(this));
}
}
// Utility method to extend defaults with user options
function extendDefaults(source, properties) {
var property;
for (property in properties) {
if (properties.hasOwnProperty(property)) {
source[property] = properties[property];
}
}
return source;
}
// Utility method to determine which transistionend event is supported
function transitionSelect() {
var el = document.createElement('div');
if (el.style.WebkitTransition) return 'webkitTransitionEnd';
if (el.style.OTransition) return 'oTransitionEnd';
return 'transitionend';
}
}());
/*
|
| Usage
| var modal = new Modal({
| closeButton: false,
| content: '<div>HTML content can go here!</div>',
| maxWidth: 600,
| overlayClose: false
| });
|
| modal.open();
|
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment