DEV Community

Cover image for Create a JavaScript library. Add callbacks
Alex Shulaev
Alex Shulaev

Posted on

Create a JavaScript library. Add callbacks

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.

GitHub logo 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)