When you want to randomly order elements on a website, server-side solutions like Kirby's shuffle method are neat.
Recently I used it for an amazing project by Verlage gegen Rechts. They collected hundreds of diverse posters against discrimination and hate and wanted to display them in random order to give each one equal visibility.
Unfortunately, shuffling in the backend doesnāt work well with caching ā the order gets āstuckā until the cache is cleared. And in this case I definitely needed caching. It makes me super happy to see how much traffic that website gets, but it also means I need to be very careful with performance.
So hereās a simple frontend solution:
The HTML setup
We use a data-shuffle
attribute to mark the container whose children we want to shuffle:
<div data-shuffle>
<div>First</div>
<div>Second</div>
<div>Third</div>
</div>
Prevent flash of unsorted content
To avoid seeing the original order briefly before JavaScript runs, we hide the elements initially.
The @media (scripting: enabled)
query keeps elements visible when JavaScript is disabled. For users with scripting enabled, elements become visible once the data-shuffle
attribute is set to shuffled
.
@media (scripting: enabled) {
[data-shuffle] > * {
visibility: hidden;
}
[data-shuffle="shuffled"] > * {
visibility: inherit;
}
}
Itās important to stress that Iām not using the media query to prevent the flash of unsorted content. Iām using JavaScript to trigger the visibility change. Thanks to Vadim on Mastodon for the feedback on my wording.
The JavaScript shuffler
This lightweight class handles the shuffling:
export class Shuffler {
constructor() {
document.querySelectorAll('[data-shuffle]').forEach(container => {
this.shuffleContainer(container);
});
}
shuffleContainer(container) {
const elements = [...container.children];
// Fisher-Yates shuffle
let i = elements.length;
while (i) {
const j = Math.floor(Math.random() * i--);
[elements[i], elements[j]] = [elements[j], elements[i]];
}
// Update DOM efficiently
const fragment = document.createDocumentFragment();
elements.forEach(element => fragment.appendChild(element));
container.appendChild(fragment);
// Show the elements (with CSS)
container.dataset.shuffle = 'shuffled';
}
}
I use the Fisher-Yates algorithm for unbiased shuffling and a DocumentFragment for performance.
Usage
Import the class and instantiate it:
import { Shuffler } from './Shuffler.js';
new Shuffler();
Thatās it! The elements will be randomly ordered on each page load, even with caching enabled.
Is it even a good idea?
Yes, asking this question now seems a bit late. But random ordering simply isnāt always the right choice ā it can be confusing when users expect a specific order or need to find items quickly. But when you want to give equal visibility to all elements, like we did for Verlage gegen Rechts, it can be a good solution.
Feedback
I was recently looking into webmentions for a different project. Iām still not sure whether I want to implement it for my own website. Seems like a lot of work. For now, you can ācommentā on Mastodon.
Let me know if you have any questions or feedback!