Odo Dialog
Responsive window dialog component focused on a11y and extendibility
Support
IE9+ with polyfills.
To support IE<=11, you will need to add classList
and Element#closest
polyfills.
Dependencies
Odo Helpers
Styles
This component has required css - css/odo-dialog.css.
Default Dialogs
By default, open dialogs have all the content centered within the screen, as well as have a fade animation for opening/closing.
The default, unstyled dialog
A styled dialog
See the demo.css for the styles.
<button data-odo-dialog-open="default">Open the default dialog</button>
<!-- Could be elsewhere in your markup -->
<div class="odo-dialog" id="default" role="dialog" aria-labelledby="default-title" aria-hidden="true">
<div class="odo-dialog__content" role="document">
<div class="odo-dialog__inner">
<h2 id="default-title">Cat Ipsum</h2>
<p>Generate some placeholder text with <a href="http://www.catipsum.com/" target="_blank" rel="noopener">Cat Ipsum</a>!</p>
<p>Hunt by meowing loudly at 5am next to human slave food dispenser scream at teh bath. Friends are not food. Mew love to play with owner's hair tie brown cats with pink ears wake up human for food at 4am. Sit in window and stare ooo, a bird! </p>
<button type="button" class="odo-dialog__close" data-odo-dialog-close aria-label="Close this dialog window">×</button>
</div>
</div>
</div>
const defaultDialog = new OdoDialog(document.getElementById('default'));
Public Events
The opened event is triggered when the content has finished its transition in, while the closed event triggers once the dialog has completely transitioned out. In case you need to retrieve some information from the button which triggered the dialog to open, you can subscribe to the TRIGGER_CLICKED
event.
defaultDialog.on(OdoDialog.EventType.OPENED, function () {
console.log('default dialog opened');
});
defaultDialog.on(OdoDialog.EventType.CLOSED, function () {
console.log('default dialog closed');
});
defaultDialog.on(OdoDialog.EventType.TRIGGER_CLICKED, function (triggerElement) {
console.log('dialog about to open because you clicked:', triggerElement);
});
Public Methods
defaultDialog.open() // Open the dialog
defaultDialog.close() // Close the dialog
defaultDialog.dispose() // Close the dialog, remove event listeners and element references
defaultDialog.getByClass(className) // Find a single element by class inside the dialog
Public Properties
defaultDialog.element // Base element
defaultDialog.options // options object
defaultDialog.id // Unique id for the dialog. Same as the id attribute of the base element
defaultDialog.isOpen // Whether the dialog is currently open
Protected Methods & Properties
If you create a subclass of OdoDialog, you will have access to these as well*.
defaultDialog.onClick(evt) // Delegated click handler on the dialog.
defaultDialog.onKeyPress(evt) // Listens for the ESC key and traps the TAB key.
defaultDialog.onResize(viewportHeight) // Sets the height of the dialog on viewport resize.
defaultDialog.content // Dialog content (role=document)
defaultDialog.backdrop // The background behind the dialog
defaultDialog.isAnimating // Whether the dialog is currently animating (opening/closing)
*Because this is JavaScript, you have access to them already.
Changing Transitions
In addition to the default fade/scale transition, OdoDialog
comes with two other transitions: odo-dialog--fade
and odo-dialog--zoom-in
. The other options in the <select>
below are custom transitions added to this page. See the next section for details.
Add Custom Transitions
You can add your own transitions too.
OdoDialog
follows a similar transition sequence to Vue.js transition effects.
.odo-dialog
element isdisplay: none
- Something calls
open()
(either programatically or clicking a trigger) - The
odo-dialog--open
class is added to removedisplay: none
and theodo-dialog--enter
class is added. - On the next frame,
odo-dialog--enter
is removed andodo-dialog--enter-active
is added. - When the transition on the dialog finishes,
odo-dialog--enter-active
is removed.
When the dialog closes, these steps are performed [mostly] in reverse order.
.odo-dialog--fall-through {
transition-timing-function: cubic-bezier(0.19, 1, 0.22, 1);
}
.odo-dialog--fall-through.odo-dialog--enter,
.odo-dialog--fall-through.odo-dialog--leave-active {
opacity: 0;
}
.odo-dialog--fall-through.odo-dialog--enter {
transform: translateY(-30vh);
}
.odo-dialog--fall-through.odo-dialog--leave-active {
transform: translateY(30vh);
}
Modals
You can create a dialog which is not dismissable with the dismissable: false
option. When the user presses the ESC key, nothing will happen. You should also remove data-odo-dialog-close
attribute from the backdrop and don't add a close button.
const modal = new OdoDialog(document.getElementById('modal-dialog'), {
dismissable: false,
});
const button = modal.element.querySelector('button');
button.addEventListener('click', () => {
modal.close();
});
Using a different scrollable section
OdoDialog
internally uses a component called ScrollFix
. This component prevents mobile browsers from scrolling the page behind the dialog when the scrollable region is at the top or bottom of the scrollable area. In order to make a different element scrollable, the ScrollFix
must be applied to that new region instead of the original main element.
You can choose a different region to be scrollable with the scrollableElement
option. Give it a selector, like: scrollableElement: '.my-list'
. Setting scrollableElement
to null
will disable ScrollFix
completely.
Fullscreen
Add the .odo-dialog--full
class to the main element to make it take up the whole screen. It sets the width
and height
of .odo-dialog__content
and .odo-dialog__inner
elements to inherit
.
<div class="odo-dialog odo-dialog--full odo-dialog--fade" id="fullscreen" aria-hidden="true">…</div>
Global Dialog Instantiation
On a project, you may have different dialogs, but they are all of the same type. Instead of initializing each one individually, you can run the initializeAll()
method, which accepts the same options as instantiating a dialog individually, but applies them to all dialogs. It will return an array of OdoDialog
instances.
OdoDialog.initializeAll(options);
Dialog Positioning
The dialog element is display: flex
and the content element has margin: auto
. This centers the content element within the viewport.
Unfortunately, the auto margin does not work in IE11 and below because the content element does have an explicit height set on it. In this case, a class of odo-dialog--no-auto-margin
is added to the main element and OdoDialog.SUPPORTS_AUTO_MARGINS
will be false
. If you need to provide a fallback for centering in IE11, you can use either of those hooks.
This gist could provide you with a starting point.
Extending OdoDialog
OdoDialog is built to be extended. Let's create a dialog which closes after scrolling it mostly out of view. Check the demo.js for the compiled code or GitHub for the original ES6 version.
Dialogs inside dialogs
Dialogs can have buttons inside them which open another dialog without closing the current one.