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);
}
}
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();
})
});
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 :)