Written in ECMAScript 2015 and transpiled for universal use with Babel 6.
Contributions welcome.
new VanillaModal()
will now benew VanillaModal.default()
. This is due to the way in which Babel 6 handles default exports.The semantically-unsound
[rel="modal:open"]
and[rel="modal:close"]
default listeners have been deprecatedThey have been replaced by
[data-modal-open]
and[data-modal-close]
. Please update your codebase if you were not supplyingopen
orclose
parameters to the modal.
MIT. Please feel free to offer any assistance - pull requests, bug tracking, suggestions are all welcome. The issue tracker is over here.
I was pretty fed up looking for a modal script that wasn't variously:
a) Bloated. b) Inaccessible. c) Needlessly complicated. d) Riddled with third party dependencies (here's looking at you, jQuery). e) Trying to hijack beautiful hardware-accelerated CSS transitions using JavaScript.
Since the modal's open
and close
event listeners are delegated from the document, you can use this script with any client-side routing or DOM-affecting framework.
If you're concerned about garbage collection, you may be pleased to know there's a modal.destroy()
method baked in, which removes all internal events and references.
-
Using NPM:
npm install vanilla-modal --save
-
Using Bower:
bower install vanilla-modal --save
-
The script is compiled using UMD module declarations. Use it with Webpack, Browserify, RequireJS or by simply including a
<script>
tag. -
ES 2015
import VanillaModal from 'vanilla-modal';
-
CommonJS:
const VanillaModal = require('vanilla-modal');
-
AMD
require(['/node_modules/vanilla-modal/dist/index.js'], function(VanillaModal) { const vanillaModal = new VanillaModal(); });
-
Browser
<script src="/node_modules/vanilla-modal/dist/index.js"></script> <script>var vanillaModal = new VanillaModal.default()</script>
This part is important. Vanilla Modal doesn't use any template strings or DOM building algorithms (although this is on the roadmap for version 2).
As a result, you will need to add your modal's container HTML to your document - by using JavaScript ahead of the modal's instantiation, or by writing HTML into your document.
The payoff is that you can make the modal look any way you wish.
<div class="modal">
<div class="modal-inner">
<a data-modal-close>Close</a>
<div class="modal-content"></div>
</div>
</div>
Following this, create some off-screen containers to house your modal's content. Give each an ID to make them selectable via anchor elements, and accessible using JavaScript-disabled browsers.
The modal will pick up the contents that are inside the container with the ID specified by the triggering anchor's href
attribute. It will place them in the modalContent
container specified by your settings object. In the example above, the default container class of .modal-content
is used.
<div id="modal-1" class="modal-hider">Modal 1 content</div>
<div id="modal-2" class="modal-hider">Modal 2 content</div>
Note: Vanilla Modal applies the class specified by
loadClass
to thepage
element. Both are specified in settings, and default respectively tovanilla-modal
andbody
. This is done in order to make the modal as accessible as possible for all use cases.
<style type="text/css">
body.vanilla-modal .modal-hider {
position: absolute;
left: -99999em;
}
</style>
const modal = new VanillaModal(options);
(Where
options
is a configuration hash. The full list of options, as well as their defaults, are listed below under the "Options and Defaults" heading.)
Vanilla Modal handles display logic using CSS. Hardware acceleration via CSS transforms comes highly recommended, for a smooth device-agnostic experience.
Two things to keep in mind:
-
Using
display: none;
on any element will efface transitions you might otherwise wish to use. -
Whatever property you're using when closing the modal (
z-index
in the example below) will need atransition-length
of0
and atransition-delay
property of the length of the longest other transition. This prevents the modal's obfuscating property from kicking in ahead of the closing animation (e.g. changing thez-index
before theopacity
animation has played out).
transition: opacity 0.2s, z-index 0s 0.2s;
Default delegate targets are as follows:
-
[data-modal-open]
triggersmodal.open()
. -
[data-modal-close]
triggersmodal.close()
.
Examples follow:
The following element will open #modal-1
using VanillaModal.
<a href="#modal-1" data-modal-open>Modal 1</a>
The element below will close the modal.
<a data-modal-close>Close</a>
These defaults can easily be changed at instantiation:
const modal = new VanillaModal({
open: '.my-open-class',
close: '.my-close-class'
});
If you need to open the modal automatically, you can do so by passing a DOM ID string to the open()
function.
For example:
const modal = new VanillaModal();
modal.open('#foo');
The modal can likewise be closed programmatically using the close()
method.
-
{Object} $
A hash of DOM nodes used internally by the modal. Useful if at any stage the modal's container needs to change.
-
{Object} $$
The modal's settings object.
-
{Boolean} isOpen
true
if the modal is open. -
{Node} current
The DOM node currently displayed in the modal.
null
if not set. -
{Function} close()
The modal's callable
close
method. -
{Function} open(String)
The modal's callable
open
method. This requires the passed DOM ID target to be present on the page. -
{Function} destroy()
Closes the modal and removes all event listeners and internal references. This releases an instantiated modal to the next garbage collection cycle.
The options object contains DOM selector strings and bindings.
Defaults are overridden by providing an options
object to a new VanillaModal instance.
Note: this API is feature-frozen for the 1.x release, but subject to change at 2.x.
{
modal: '.modal',
modalInner: '.modal-inner',
modalContent: '.modal-content',
open: '[data-modal-open]',
close: '[data-modal-close]',
page: 'body',
loadClass: 'vanilla-modal',
class: 'modal-visible',
clickOutside: false,
closeKeys: [27],
transitions: true,
onBeforeOpen: null,
onBeforeClose: null,
onOpen: null,
onClose: null
}
-
{String} modal
The class of the outer modal container. This is usually a fixed position element that takes up the whole screen. It doesn't have to be, though - the modal can take the form of a toast popup, for example, or any type of overlay you can think of.
-
{String} modalInner
The inner container of the modal. This usually houses a close button at the very least (see HTML above). It should also contain the
modalContent
element. -
{String} modalContent
The container used to house the modal's content when it's transferred to the modal. This must be a child of
modalInner
. -
{String} open
The selector to bind the
open()
event to. -
{String} close
The selector to bind the
close()
event to. -
{String} page
The outermost DOM selector to apply the
loadClass
andclass
classes to. This isbody
by default but could just as easily behtml
ormain
in any common web app. -
{String} loadClass
The class to apply to the
page
DOM node at the moment the script loads. -
{String} class
The class to apply to the
parent
container when the modal is open. -
{Boolean} clickOutside
If set to
true
, a click in the area outside themodalInner
container will fire aclose()
event. -
{Array} closeKeys
Hitting any keycodes contained within this array while the modal is open will fire a
close()
event. Set this tofalse
or an empty array to disable keyboard modal closure. Defaults to [27], which isesc
on a traditional keyboard. -
{Boolean} transitions
If set to
false
, the modal will treat every browser like IE 9 and ignore transitions when opening and closing. -
{Function} onBeforeOpen
{Function} onBeforeClose
{Function} onOpen
{Function} onClose
Hooks that fire before their respective events. These are context-bound to the VanillaModal instance, and receive their triggering events (e.g.
click
orkeydown
) as its only arguments.
This script works in the evergreen mobile & desktop browsers, as well as IE 11, 10, and 9 (the last has no support for transitions).
It is not compatible with Opera Mini or the Blackberry browser, and there are currently no plans afoot to support either.