plain-modal

The simple library for customizable modal window.

Usage no npm install needed!

<script type="module">
  import plainModal from 'https://cdn.skypack.dev/plain-modal';
</script>

README

PlainModal

npm GitHub issues dependencies license

The simple library for fully customizable modal window in a web page.

Document and Examples https://anseki.github.io/plain-modal/

PlainModal has basic functions only, and it does nothing about visual style of the modal window. It means that you can free style it to perfect match for your web site or your app. It has no image files and no CSS files, it is just one small file only.

Features:

  • Make a specified element become a modal window, and control the opening and closing it.
  • Cover a web page with an overlay, and block scrolling, focusing and accessing anything under the overlay by a mouse or keys.
  • No dependency.
  • Single file.
  • Modern browsers are supported. (If you want to support legacy browsers such as IE 9-, see jQuery-plainModal.)

Usage

Load PlainModal into your web page.

<script src="plain-modal.min.js"></script>

This is simplest case:

<div id="modal-content" style="background-color: white;">
  Hello, world!
</div>
var modal = new PlainModal(document.getElementById('modal-content'));
modal.open();

Now, new modal window is opened.
You will see that the modal window has no style except for background-color to improve the visibility of this example. Therefore you can free style it. In other words, you have to do that for the visual design you want.

For options and more details, refer to the following.

Constructor

modal = new PlainModal(content[, options])

The content argument is an element that is shown as a modal window.
The modal window is initially closed. That is, the content element of the constructed PlainModal is being hidden.
To hide the content element until the web page is ready, you can apply display: none to the content element before the constructing PlainModal. PlainModal updates the display if it is none.

The options argument is an Object that can have properties as options. You can also change the options by setOptions or open methods or properties of the PlainModal instance.

For example:

// Construct new modal window, with `duration` option.
var modal = new PlainModal(document.getElementById('modal-content'), {duration: 400});

Methods

open

self = modal.open([force][, options])

Open the modal window.
If true is specified for force argument, open it immediately without an effect. (As to the effect, see duration option.)
If options argument is specified, call setOptions method and open the modal window. It works the same as:

modal.setOptions(options).open();

close

self = modal.close([force])

Close the modal window.
If true is specified for force argument, close it immediately without an effect. (As to the effect, see duration option.)

setOptions

self = modal.setOptions(options)

Set one or more options.
The options argument is an Object that can have properties as options.

Options

closeButton

Type: Element or undefined
Default: undefined

Bind close method to specified element, and the modal window is closed when the user clicks the element.

For example:

<div id="modal-content" style="background-color: white;">
  Hello, world!
  <div><button id="close-button">CLOSE</button></div>
</div>
modal.closeButton = document.getElementById('close-button');

duration

Type: number
Default: 200

A number determining how long (milliseconds) the effect (fade-in/out) animation for opening and closing the modal window will run.

overlayBlur

Type: number or boolean
Default: false

Applies a Gaussian blur to the web page while the overlay is shown. Note that the current browser might not support it.
It is not applied if false is specified.

For example:

modal.overlayBlur = 3;

dragHandle

Type: Element or undefined
Default: undefined

To make the modal window be draggable, specify a part element of the content element (or the content element itself) that receives mouse operations. A user seizes and drags this element to move the modal window.
The content element itself can be specified, and all of the modal window can be seized and dragged.

For example:

modal.dragHandle = document.getElementById('title-bar');

openEffect, closeEffect

Type: function or undefined
Default: undefined

By default, the modal window is opened and closed with the fade-in effect and fade-out effect animation. You can specify additional effect for openEffect and closeEffect option. The default fade-in/out effect also still runs, specify 0 for duration option if you want to disable the default effect.

Each effect is a function that is called when the modal window is opened and closed. The function do something to open/close the modal window. It usually starts an animation that the modal window appears or it vanishes. For example, it adds or removes a CSS class that specifies CSS Animation.

The function may be passed done argument that is callback function.
When the done is passed, the current opening or closing runs asynchronously, and the function must call the done when the effect finished.
On the other hand, when the done is not passed, the current opening or closing runs synchronously, and the function must make the modal window appear or vanish immediately without effect. The opening or closing runs synchronously when open method or close method is called with force argument, or a child modal window is closed by closing parent modal window.

In the functions, this refers to the current PlainModal instance.

For example:

var content = document.getElementById('modal-content'),
  modal = new PlainModal(content, {
    openEffect: function(done) {
      if (done) { // It is running asynchronously.
        startOpenAnim(); // Show your animation 3 sec.
        setTimeout(function() {
          stopOpenAnim();
          content.style.display = 'block';
          done(); // Tell the finished to PlainModal.
        }, 3000);

      } else { // It is running synchronously.
        stopOpenAnim(); // It might be called when it is already running.
        content.style.display = 'block'; // Finish it immediately.
      }
    },

    closeEffect: function(done) {
      if (done) { // It is running asynchronously.
        startCloseAnim(); // Show your animation 1 sec.
        setTimeout(function() {
          stopCloseAnim();
          content.style.display = 'none';
          done(); // Tell the finished to PlainModal.
        }, 1000);

      } else { // It is running synchronously.
        stopCloseAnim(); // It might be called when it is already running.
        content.style.display = 'none'; // Finish it immediately.
      }
    }
  });

You might want to use CSS animation such as CSS Transitions for the effect, and you might want to use DOM events such as transitionend event to get the finishing the effect. (You should consider cross-browser compatibility when you use those. It will be described later.)
Note that you should add an event listener only once because the DOM events allow adding multiple listeners for an event. If you will use addEventListener in the openEffect or closeEffect function, you need something like a flag to avoid multiple adding.
Or, you can get the callback function via effectDone property instead of done argument. This is useful for using effects with DOM events. You can get it at outer of the openEffect and closeEffect functions also. And it is a property, not a method. Therefore you can pass it to addEventListener directly without wrapping by a function. And also, it can be used for both the openEffect and closeEffect functions.

For example:

.modal {
  margin-top: -600px; /* Default position: out of view */
  transition: margin-top 1s;
}

.opened {
  margin-top: 0; /* Move to the center position */
}

.force {
  transition-property: none; /* Disable the animation */
}
var content = document.getElementById('modal-content'),
  modal = new PlainModal(content, {
    openEffect: function(done) {
      content.classList.toggle('force', !done); // Switch by async/sync.
      content.classList.add('opened');
    },
    closeEffect: function(done) {
      content.classList.toggle('force', !done); // Switch by async/sync.
      content.classList.remove('opened');
    }
    // You will probably collect 2 functions into single function
    // with `state` and `toggle`.
  });

// Tell the finished to PlainModal when the event is fired.
content.addEventListener('transitionend', modal.effectDone, true); // Add only once.

Note that you should consider cross-browser compatibility if you will use CSS Transitions or element.classList.
These may help you for cross-browser compatibility.

In addition, if you will use other transitions also to do something, the listener will be code like:

// `timedTransitionEnd` instead of `transitionend`, for cross-browser compatibility
content.addEventListener('timedTransitionEnd', function(event) {
  if (event.target === content && event.propertyName === 'margin-top') {
    modal.effectDone();
  }
}, true);

onOpen, onClose, onBeforeOpen, onBeforeClose

Type: function or undefined
Default: undefined

Event listeners:

  • onBeforeOpen is called when the modal window is about to be opened. If false is returned, the opening is canceled.
  • onOpen is called when an opening effect of the modal window is finished.
  • onBeforeClose is called when the modal window is about to be closed. If false is returned, the closing is canceled.
  • onClose is called when a closing effect of the modal window is finished.

In the functions, this refers to the current PlainModal instance.

For example:

var modal = new PlainModal({
  onOpen: function() {
    this.closeButton.style.display = 'block';
    name.focus(); // Activate the first input box.
  },
  onBeforeClose: function() {
    if (!name.value) {
      alert('Please input your name');
      return false; // Cancel the closing the modal window.
    }
  }
});

Properties

state

Type: number
Read-only

A number to indicate current state of the modal window.
It is one of the following static constant values:

  • PlainModal.STATE_CLOSED (0): The modal window is being closing fully.
  • PlainModal.STATE_OPENING (1): An opening effect of the modal window is running.
  • PlainModal.STATE_OPENED (2): The modal window is being opening fully.
  • PlainModal.STATE_CLOSING (3): A closing effect of the modal window is running.
  • PlainModal.STATE_INACTIVATING (4): An inactivating effect of the modal window is running.
  • PlainModal.STATE_INACTIVATED (5): The modal window is being inactivating fully.
  • PlainModal.STATE_ACTIVATING (6): An activating effect of the modal window is running.

A modal window is inactivated and activated by a child modal window. For details, see "Child and Descendants".

For example:

openButton.addEventListener('click', function() {
  if (modal.state === PlainModal.STATE_CLOSED ||
      modal.state === PlainModal.STATE_CLOSING) {
    modal.open();
  }
}, false);

closeButton

Get or set closeButton option.

duration

Get or set duration option.

overlayBlur

Get or set overlayBlur option.

dragHandle

Get or set dragHandle option.

openEffect, closeEffect

Get or set openEffect, closeEffect options.

effectDone

Type: function
Read-only

See openEffect, closeEffect options.

onOpen, onClose, onBeforeOpen, onBeforeClose

Get or set onOpen, onClose, onBeforeOpen, onBeforeClose options.

Child and Descendants

When a modal window was already opened and another modal window is opened, now, the new one is the first one's "child modal window" and the first one is the new one's "parent modal window". Also, more modal windows can be opened, and those are descendants modal windows.

When a child modal window is opened, its parent modal window is moved to under the overlay that the user can't touch, then that is inactivated. And the child modal window is put on the foreground, then that is activated.
When the child modal window is closed, its parent modal window is activated again.
Descendants modal windows also work in the same way. That is, only one modal window can be active one that the user can touch. The active modal window is one that was last opened or a parent modal window that was last activated by closing its child modal window.

When a parent modal window is closed, its child modal window is closed before. The child modal window is closed with force.
Descendants modal windows also work in the same way. That is, when a modal window is closed, all its descendants modal windows are closed.

PlainModal.closeByEscKey

By default, when the user presses Escape key, the current active modal window is closed.
If you want, set PlainModal.closeByEscKey = false to disable this behavior.

PlainModal.closeByOverlay

By default, when the user clicks an overlay, the current active modal window is closed.
If you want, set PlainModal.closeByOverlay = false to disable this behavior.

Style of overlay

If you want to change style of the overlay, you can define style rules with .plainmodal .plainmodal-overlay selector in your style-sheet.
Note that some properties that affect the layout (e.g. width, border, etc.) might not work or those might break the overlay.

For example, CSS rule-definition for whity overlay:

.plainmodal .plainmodal-overlay {
  background-color: rgba(255, 255, 255, 0.6);
}

For example, for background image:

.plainmodal .plainmodal-overlay {
  background-image: url(bg.png);
}

See Also

  • PlainOverlay : The simple library for customizable overlay which covers all or part of a web page.
  • PlainDraggable : The simple and high performance library to allow HTML/SVG element to be dragged.

Thanks for images: GitHub, The Pattern Library