Vanilla Modal
See the demo.
A tiny, flexible, completely dependency-free CSS-powered JavaScript modal.
Written in ECMAScript 2015 and transpiled for universal use with Babel 6.Contributions welcome.
Please be aware of recent breaking changes.
If importing using a
<script/>
tagnew VanillaModal()
will now benew VanillaModal.default()
. This is due to the way in which Babel 6 handles default exports.The semantically-unsound
They have been replaced by[rel="modal:open"]
and[rel="modal:close"]
default listeners have been deprecated[data-modal-open]
and[data-modal-close]
. Please update your codebase if you were not supplyingopen
orclose
parameters to the modal.
License
MIT. Please feel free to offer any assistance - pull requests, bug tracking, suggestions are all welcome. The issue tracker is over here.Q & A
Why?
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.
Can I integrate this with a single page app framework?
Since the modal'sopen
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.Usage and Examples
- Install the script.
- Using NPM:
- Using Bower:
- Include the script in your project.
- The script is compiled using UMD module declarations. Use it with Webpack, Browserify, RequireJS or by simply including a
<script>
tag.
- ES 2015
- CommonJS:
```javascript
const VanillaModal = require('vanilla-modal');
```
- AMD
```javascript
require(['/node_modules/vanilla-modal/dist/index.js'], function(VanillaModal) {
const vanillaModal = new VanillaModal();
});```
- Browser
```html
<script src="/node_modules/vanilla-modal/dist/index.js"></script>
```
- Create the modal's container using HTML.
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 byBoth are specified in settings, and default respectively toloadClass
to thepage
element.
vanilla-modal
and body
.
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>
- Create a VanillaModal instance.
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.)
- Add your own CSS rules.
Here's the demo's stylesheet.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;
- Delegation and Built-in Methods
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'
});
- Programmatically opening a modal
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.VanillaModal Public Properties
{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.
Options and Defaults
The options object contains DOM selector strings and bindings. Defaults are overridden by providing anoptions
object to a new VanillaModal instance.Note: this API is feature-frozen for the 1.x release, but subject to change at 2.x.
Defaults:
{
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
and class
classes to.
This is body
by default but could just as easily be html
or main
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 the modalInner
container will fire a close()
event.{Array} closeKeys
Hitting any keycodes contained within this array while the modal is open will fire a
close()
event.
Set this to false
or an empty array to disable keyboard modal closure. Defaults to 27, which is esc
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
or keydown
) as its only arguments.Compatibility
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.