Building Your Own JavaScript Modal Plugin

Introduction

When starting a new project, there are two staple Javascript UI components that you will likely require. The first being a carousel, which I've already
taken care of, and the second being a modal. Today we are going to build out a flexible CSS3 modal plugin. Here's a demo to see what we'll be building:

Getting Started

The difference between building a plugin and a project component lies in
flexibility. The first thing we are going to is take a step back and think about the requirements. Our modal plugin should:

Launch different modals based upon option sets

Allow users to define custom transitions

Be responsive

Have max/min width points

Anchor to the top of the page if too tall

Be centered otherwise

Accept a HTML string for content OR a domNode

Have no dependencies

See that last line? Thats right folks, we're doing this in plain old Javascript.

The Javascript

Architecture

Alright, lets get our hands dirty. Our first order of business is going to be deciding on our plugin architecture and picking a design pattern. Let's create an
IIFE to create a closure we can work within. Closures can be leveraged to create a private scope, where you have control over what data you make available.

We want to add a constructor method for our plugin, and expose it as public. Our IIFE is called globally, so our
this keyword is pointing at the window. Let's attach our constructor to the global scope using this.

Pointing our
Modal variable at a function creates a functional object, which can now be instantiated with the new keyword like so:

var myModal = new Modal();

This creates a new instance of our object. Unfortunately, our object doesn't do much at this point, so lets do something about that.

Options

Taking a look back at our requirements, our first order of business is to allow user defined options. The way we are going to achieve this is to create a set of default options, and then merge it with the object the user provides.

Pause. What's going on here? First we create global element references. These are important so that we can reference pieces of the Modal from anywhere in our plugin. Next up, we add a default options object. If a user doesn't provide options, we use these. If they do, we override them. So how do we know if they have provided options? The key here is in the
arguments object. This is a magical object inside of every function that contains an array of everything passed to it via arguments. Because we are only expecting one argument, an object containing plugin settings, we check to make sure arguments[0] exists, and that it is indeed an object. If that condition passes, we then merge the two objects using a privately scoped utility method called extendDefaults. extendDefaults takes an object, loops through its properties, and if it isn't an internal property(hasOwnProperty), assigns it to the source object. We can now configure our plugin with an options object:

var myModal = new Modal({
content: 'Howdy',
maxWidth: 600
});

Public Methods

Now that we have our
Modal object, and its configurable, how about adding a public method? The first thing a developer is going to want to do with a modal is open it up, so let's make it happen.

In order to expose a public method, we attach it to our
Modal object's prototype. When you add methods to the object's prototype, each new instance shares the same methods, rather than creating new methods for each instance. This is super performant, unless you have multi level subclassing, in which traversing the prototype chain negates your performance boost. We have also added comments and structured our component so that we have three sections: constructor, public methods & private methods. This doesn't do anything functionally, but it keeps everything organized and readable.

Core Functionality

How about we take a step back? We now have a nice plugin architecture, with a constructor, options & a public method. It is time for the bread and butter, so let's revisit what this plugin is supposed to do. Our plugin should:

Build a modal element and add it to the page

Add any classes specified in the `className` option to the modal

If the `closeButton` option is true, add a close button

If the `content` option is a HTML string, set it as the modal's content

If the `content` option is a domNode, set it's interior content as the modal's content

Set the modal's `maxWidth` and `minWidth` respectively

Add an overlay if the `overlay` option is true

When opened, add a `scotch-open` class that we can use with our CSS to define an open state.

When closed, remove the `scotch-open` class.

If the modal's height exceeds the viewport's height, also add a `scotch-anchored` class so that we can handle that scenario

Building Our Modal

We can't have a modal plugin without building a modal, so let's create a private method that constructs a modal using our defined options:

We start by getting our target content and creating a Document Fragment. A 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. If our
content is a string, we set our content variable to the option value. If our content is a domNode, we set our content variable to it's interior HTML via innerHTML. Next up, we create our actual modal element, and add our className and minWidth/maxWidth properties to it. We create it with a default scotch-modal class for initial styling. Then, based upon option values, conditionally create a close button and an overlay in the same fashion. Finally, we add our content to a content holder div, and append it to our modal element. After appending our modal to the Document Fragment and appending our Document Fragment to the body, we now have a built modal on the page!

Events

This modal (hopefully) isn't going to close itself, so providing we have a close button and/or an overlay, we need to bind events to them to make the magic happen. Below, we create a method to attach these events:

We attach our events using the
addEventListener method, passing a callback to a method we haven't created yet called close. Notice we dont just call close, but we use the bind method and pass our reference to this, which references our Modal object. This makes sure that our method has the right context when using the this keyword.

Opening the Modal

Let's head back to the public `open` method we created earlier. Time to make it shine:

When opening our modal, we first have to build it. We call our
buildOut method using the call method, similarly to the way we did in our event binding with bind. We are simply passing the proper value of this to the method. We then call initializeEvents to make sure any applicable events get bound. Now, I know you are saying to yourself, what is going on with getComputedStyle? Check this out: We are using CSS3 for our transitions. The modal hides and shows based upon applied class names. When you add an element to the dom, and then add a class, the browser might not have interpereted the initial style, so you never see a transition from its initial state. Thats where window.getComputedStyle comes into play. Calling this forces the browser to recognize our initial state, and keeps our modal transition looking mint. Lastly, we add the scotch-open class name. But that's not all. We currently have our modal centered, but if its height exceeds the viewport, that's gonna look silly. We use a ternary operator to check the heights, and if our modal is too tall, we also add the scotch-anchored class name, to handle this situation.

Closing the Modal

Like anything else that is completely amazing in this world, at some point our modal must come to an end. So let's build a method to send it to the other side:

In order to have our modal transition out, we remove its
scotch-open class name. The same applies to our overlay. But we aren't finished yet. We have to remove our modal from the DOM, but its going to look ridiculous if we don't wait until our animation has completed. We accomplish this by attaching an event listener to detect when our transition is complete, and when it is, its "Peace out, cub scout". You may be wondering where this.transitionEnd came from. I'll tell you. Browsers have different event names for transitions ending, so I wrote a method to detect which one to use, and called it in the constructor. See below:

Wrap Up

And there you have it. We have built out our modal javascript plugin. Comments and spacing aside,
we did it in 100 lines of pure, sweet Vanilla Javascript. Check out our finished product below, and then get ready to talk CSS:

The CSS

This
is a CSS3 modal, so Javascript is only half the battle. To recap, we have a base class on our modal of scotch-modal, and an open class of scotch-open. Modals that exceed the viewport height have a class of scotch-anchored, and potentially have an overlay(scotch-overlay) and a close button(scotch-close). Let's apply some base styles:

In a nutshell, we are making our modal and overlay just appear by default. We leave a 1ms transition on by default, so that we can be sure that our
transitionend event actually fires. We use the translate centering method to vertically and horizontally center our modal in the window. If scotch-anchored is applied, we center horizontally, and anchor our modal 20px from the top of the window. This is a great starting base for adding custom animations via the className option, so why don't we go ahead create a custom animation for the fade-and-drop default className of our plugin:

For our
fade-and-drop transition, we want the overlay to fade in, and the modal to drop in. We utilize the delay argument of the transition property shorthand to wait 500ms until the overlay has faded in. For our outro transition, we want the modal to fly back up out of sight and then fade the overlay out. Again, we use the delay property to wait for the modal animation to complete.

Using Our Fancy New Modal

Now we have a fully working modal plugin. Woo! So how do we actually use it? Using the
new keyword, we can create a new modal and assign it to a variable:

var myModal = new Modal();
myModal.open();

Without the
content option set, it is going to be a pretty lame modal, so lets go ahead and pass in some options:

By now if you haven't realized it, I'll spill the beans. This article isn't about writing a modal, it's about writing a plugin. If you have been following along you should have the tools required to do just this. Say you want to make the plugin open automatically when instantiated. Let's add an option for that. First, we add the option to our defaults in our constructor method.

Conclusion

I sincerely hope that after reading this, everyone learned something they didn't know before. I personally learned a number of things during the course of writing it! We now have a fully functioning CSS3 modal plugin, but don't stop here. Make it yours. Add features that you think would be helpful, craft some custom transitions, go absolutely bananas. You don't even need to make a better modal. Take the plugin building skills you acquired here today, and go build a brand new plugin. Who knows, it could be the next big thing! If you have made it this far, I appreciate your time and diligence and I look forward to writing more fun tutorials here in the future. In the meantime, checkout some cool demos below!