And here is a new part of creating a library of modal windows in JavaScript. This time we are implementing two small improvements. First, we'll add the ability to use callbacks to the configuration. And secondly, we'll improve the keyboard control. By tradition, I'm sharing a video version with you, for those who want to see how I wrote it ๐ฌ
Callback
As per MDN: "A callback function is a function passed into another function as an argument, which is then invoked inside the outer function to complete some kind of routine or action". A small use case:
const addition = (a, b) => a + b;
const multiplication = (a, b) => a * b;
const count = (a, b, callback) => callback(a, b);
console.log(count(10, 20, addition)); // 30
console.log(count(10, 20, multiplication)); // 200
In our example, the first two functions addition
andmultiplication
simply perform a mathematical action with two parameters and return the result of the calculation. But the count method takes three parameters, the first two are numbers, and the third is the action that you need to do with numbers. This is the callback. In this context, such an example may seem redundant. All the convenience of callbacks is revealed when we need to wait for any action or result
And this perfectly shows the situation that can occur when using a library with hasAnimation
. If we need to perform some kind of functionality not immediately after clicking on the button that will open the modal window, but only after it is fully opened, callbacks will help us.
Let's add this code:
constructor({
...
onOpen = () => {},
onClose = () => {},
beforeOpen = () => true,
beforeClose = () => true,
}: ConfigType) {
this.$modal = document.querySelector(selector);
this.onOpen = onOpen;
this.onClose = onClose;
this.beforeOpen = beforeOpen;
this.beforeClose = beforeClose;
...
}
close(event?: Event) {
const isContinue = this.beforeClose(event);
if (!isContinue) return;
...
this.preparationClosingModal(event);
}
preparationClosingModal(event?: Event) {
if (this.hasAnimation) {
const handler = () => {
...
this.onClose(event);
this.$modal?.removeEventListener('animationend', handler);
};
this.$modal?.addEventListener('animationend', handler);
} else {
...
this.onClose(event);
}
}
For the open method, we'll need to do the same with this.onOpen
andthis.beforeClose
.
The this.onOpen
andthis.onClose
methods play the role of events that report the corresponding action of the modal window. Such methods will be called as soon as the animation ends at the modal window (or immediately if the animation is disabled). Such methods are conveniently used, for example, to send analytics in order to track interactive user actions.
The this.beforeOpen
andthis.beforeClose
methods, as you may have noticed, have a slight difference, they should return a boolean value. This is done intentionally to add flexibility in window configuration. For example, it is convenient to use such methods to block a modal window until the animation is completed (if opening animation takes considerable time, this may be necessary), or to block the state of the window until a certain action is taken by the user (such as filling out a feedback form).
As you can see, we added just a few methods, but significantly expanded the configuration options.
Keyboard control
The main idea of โโimplementation is to prepare the library for the final parts, which will implement support for accessibility and convenient keyboard control.
This time we'll add one small action, which for me personally is very convenient. This closes the modal window by clicking on Esc
.
And if you try to look for solutions to track Esc
, you'll most likely see this code:
document.addEventListener('keyup', function (event) {
if (event.keyCode === 27) console.log('Esc button was pressed');
});
And then one interesting embarrassment happened. If you watched my video, you could see that to determine which key was pressed, I used keyCode
onKeydown(event: KeyboardEvent) {
if (event.keyCode === KEY_CODE.ESC) this.close();
}
But if you look at the code now, you'll see another solution.
It happened because keyCode
has been the standard way to determine the type of key pressed for many years. This has great support for browsers. But the fact is that now this is deprecated and it is no longer recommended to use it.
keyCode
was deprecated because in practice it was "inconsistent across platforms and even the same implementation on different operating systems or using different localizations." The new recommendation is to use key or code.
However, there are also minor difficulties, the fact is that KeyboardEvent.key
is implemented differently in different browsers. For example, in IE11 KeyboardEvent.key
uses Esc
rather than Escape
for the corresponding keyboard button, because it was implemented before the specification was completed. More detailed browser support can be found here.
This will look like an implementation with compatibility support for older browsers
export const KEY = {
ESC: 'Esc',
ESCAPE: 'Escape',
CODE: 27,
}
addEventListeners() {
document.addEventListener('keyup', this.onKeyup);
}
removeEventListeners() {
document.removeEventListener('keyup', this.onKeyup);
}
/**
* Keyboard press handler
*
* @param {KeyboardEvent} event - Event data
*/
onKeyup(event: KeyboardEvent) {
const key = event.key || event.keyCode;
if (key === KEY.ESCAPE || key === KEY.ESC || key === KEY.CODE) this.close(event);
}
However, we can leave a more compact form, since we don't need support on so many old browsers
/**
* Keyboard press handler
*
* @param {KeyboardEvent} event - Event data
*/
onKeyup(event: KeyboardEvent) {
if (event.key === KEY.ESCAPE || event.key === KEY.ESC) this.close(event);
}
Now, with the modal window open, we have a handler for clicking the Esc key on the keyboard. This handler calls the close
method and after closing the modal window we remove the click handler. You can see the complete solution in the repository.
Alexandrshy / keukenhof
Lightweight modal library ๐ท
Keukenhof
Micro library for creating beautiful modal windows
Table of Contents
Installation
For install, you can use npm or yarn:
npm install keukenhof
yarn add keukenhof
CDN link
<script src="https://unpkg.com/keukenhof@1.1.0/dist/index.js"></script>
Example
<div id="modal" class="modal">
<div class="modal__overlay" data-keukenhof-close></div>
<div class="modal__container" role="dialog">
<div class="modal__wrapper">
<button
class="modal__button modal__button--close"
data-keukenhof-close
>
Close modal
</button>
<main class="modal__main">
<h2 class="modal__title">Title</h2>
<p class="modal__desc">
Lorem ipsum dolor sit amet consectetur adipisicing elit
</p>
<a href="#" class="modal__link">More information</a>
โฆNext time we will consider a very extensive topic of accessibility when working with modal windows. Subscribe, it'll be interesting! See you soon ๐
Top comments (0)