3

Event Queues and Buffering Functions with JavaScript

 1 month ago
source link: https://khalidabuhakmeh.com/event-queues-and-buffering-functions-with-javascript
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
April 16, 2024

Event Queues and Buffering Functions with JavaScript

Event Queues and Buffering Functions with JavaScript
Photo by Ryan Stone

I was testing JetBrains’ latest Full Line Code Completion plugin in WebStorm by writing a quick JavaScript demo, and by the end, I was pretty happy with what I had built. The JavaScript uses a queue that pushes events to change classes on buttons in 250ms intervals to create a smooth color transition from one style to another.

Is it practical? I don’t know, but it sure is fun. I’ve included all the demo’s contents in a single file so you can test it locally. By the end of this post, you’ll have seen an event queue and how to use it in your JavaScript.

Write a JavaScript Event Queue

The premise of my event queue is straightforward. Push events into a queue and have them execute on a set universal interval. I also wanted the ability to clear the queue of any remaining events.

class EventQueue {
    constructor(interval) {
        this.queue = [];
        this.interval = interval;
        this.startProcessing();
    }

    enqueue(eventFunction) {
        this.queue.push(eventFunction);
    }

    clear() {
        this.queue = [];
    }

    startProcessing() {
        setInterval(() => {
            if (this.queue.length > 0) {
                const eventFunction = this.queue.shift();
                eventFunction();
            }
        }, this.interval);
    }
}
JavaScript

We use the setInterval function to check the queue at our designated interval to process any remaining events. Let’s start using it.

Transitioning Button Colors on Mouse Move

Before we write some more JavaScript, let’s look at the HTML elements we’ll change. Note that I’m using the Bulma CSS library and its button styling classes.

<div class="columns">
    <div class="column">
        <button class="m-1 button is-info">🐁 Move</button>
        <button class="m-1 button is-success">🐁 Move</button>
        <button class="m-1 button is-warning">🐁 Move</button>
        <button class="m-1 button is-danger">🐁 Move</button>
        <button class="m-1 button is-primary">🐁 Move</button>
        <button class="m-1 button is-link">🐁 Move</button>
        <button class="m-1 button is-info">🐁 Move</button>
        <button class="m-1 button is-success">🐁 Move</button>
        <button class="m-1 button is-warning">🐁 Move</button>
        <button class="m-1 button is-danger">🐁 Move</button>
        <button class="m-1 button is-primary">🐁 Move</button>
        <button class="m-1 button is-link">🐁 Move</button>
    </div>
</div>

Also, to smooth the transition between styles, I wrote an additional CSS style to ease the background-color to limit the strobing effect that might happen if colors change too quickly.

<style>
    .button {
        transition: background-color 0.2s ease-in-out;
    }
</style>

Let’s write some JavaScript. We want to change the CSS class on each .button as the user moves the mouse. If the user stops moving the mouse or leaves the bounds of the page, we want to clear all remaining events from our queue.

window.addEventListener('DOMContentLoaded', () => {

    const colors = [
        "is-info", "is-success",
        "is-warning", "is-danger",
        "is-primary", "is-link"
    ];

    const queue = new EventQueue(250 /* ms */);
    const changeButtonColors = () => {
        const buttons = document.querySelectorAll(".button");
        buttons.forEach(button => {
            button.classList.forEach((c) => {
                if (c.startsWith("is-")) {
                    const currentIndex = colors.indexOf(c);
                    const newIndex = currentIndex < colors.length - 1
                        ? currentIndex + 1 : 0;
                    button.classList.remove(c);
                    button.classList.add(colors[newIndex]);
                }
            });
        });
    };

    document.addEventListener('mousemove', (e) => {
        if (e.movementX === 0 && e.movementY === 0) {
            queue.clear();
        } else {
            queue.enqueue(changeButtonColors);
        }
    });

    document.addEventListener('mouseleave', () => {
        queue.clear();
    })
});
JavaScript

That’s pretty straightforward. As a personal side note, JavaScript and the web stack have come a long way. This was genuinely a joy to write.

Let’s see what it’s like in action.

Pretty cool!

Here’s the full HTML/JavaScript/CSS combination in one file.

<!DOCTYPE html>
<meta name="viewport" content="width=device-width, initial-scale=1">
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <!-- bulma.io -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.9.4/css/bulma.min.css">
    <style>
        .button {
            transition: background-color 0.2s ease-in-out;
        }
    </style>
</head>
<body>
<section class="section">
    <div class="container">
        <h1 class="title">
            Hello World
        </h1>
        <p class="subtitle">
            My first website with <strong>Bulma</strong>!
        </p>
        <div class="columns">
            <div class="column">
                <button class="m-1 button is-info">🐁 Move</button>
                <button class="m-1 button is-success">🐁 Move</button>
                <button class="m-1 button is-warning">🐁 Move</button>
                <button class="m-1 button is-danger">🐁 Move</button>
                <button class="m-1 button is-primary">🐁 Move</button>
                <button class="m-1 button is-link">🐁 Move</button>
                <button class="m-1 button is-info">🐁 Move</button>
                <button class="m-1 button is-success">🐁 Move</button>
                <button class="m-1 button is-warning">🐁 Move</button>
                <button class="m-1 button is-danger">🐁 Move</button>
                <button class="m-1 button is-primary">🐁 Move</button>
                <button class="m-1 button is-link">🐁 Move</button>
            </div>
        </div>
    </div>
</section>
<script type="application/javascript">
window.addEventListener('DOMContentLoaded', () => {

    const colors = [
        "is-info", "is-success",
        "is-warning", "is-danger",
        "is-primary", "is-link"
    ];

    const queue = new EventQueue(250 /* ms */);
    const changeButtonColors = () => {
        const buttons = document.querySelectorAll(".button");
        buttons.forEach(button => {
            button.classList.forEach((c) => {
                if (c.startsWith("is-")) {
                    const currentIndex = colors.indexOf(c);
                    const newIndex = currentIndex < colors.length - 1
                        ? currentIndex + 1 : 0;
                    button.classList.remove(c);
                    button.classList.add(colors[newIndex]);
                }
            });
        });
    };

    document.addEventListener('mousemove', (e) => {
        if (e.movementX === 0 && e.movementY === 0) {
            queue.clear();
        } else {
            queue.enqueue(changeButtonColors);
        }
    });

    document.addEventListener('mouseleave', () => {
        queue.clear();
    })
});

    class EventQueue {
        constructor(interval) {
            this.queue = [];
            this.interval = interval;
            this.startProcessing();
        }

        enqueue(eventFunction) {
            this.queue.push(eventFunction);
        }

        clear() {
            this.queue = [];
        }

        startProcessing() {
            setInterval(() => {
                if (this.queue.length > 0) {
                    const eventFunction = this.queue.shift();
                    eventFunction();
                }
            }, this.interval);
        }
    }

</script>
</body>
</html>

Conclusion

You can create interactive experiences with some JavaScript and some CSS. The EventQueue I wrote is surprisingly simple yet powerful for my use case, but you can adapt it to your needs. The definition of transition is also an excellent tool for keeping visual transitions from being jarring or obnoxious.

Thanks for reading, and I hope you have a great day. Oh, if you thought this post was cool or helpful, please remember to share it with friends and colleagues. Cheers :)


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK