Just simple CSS, JS and HTML marquee.
You can duplicate as many marquee as you want on the same page easy to setup thanks to html attributes 🙂
Setup your html thats it.
<div class="marquee-container"
data-text="White Label the Name, Custom Post Types, Custom Fields, Custom Taxonomies, Security Settings, Block Editor Settings, Disable File Editing, Disable WP JSON If Not Logged In"
data-font-size="50"
data-speed="20"
data-direction="left">
</div>
<div class="marquee-container"
data-text="SMTP Settings, Mail Logs, 404 Logs, Custom Login/Register Page, 301 Redirects, Remove WP Version, WP Revision Limit"
data-font-size="50"
data-speed="25"
data-direction="right">
</div>
<style>
.marquee-container {
overflow: hidden;
white-space: nowrap;
margin-bottom: 20px;
}
.marquee {
display: inline-block;
white-space: nowrap;
}
.marquee-text {
display: inline-block;
white-space: nowrap;
color: #00000044;
line-height: 1;
font-weight: 200;
}
</style>
<script>
class Marquee {
constructor(container, settings) {
this.container = container;
this.settings = settings;
this.animationStyleElement = null;
this.init();
}
// Initialize the marquee: set font size and create the track element
init() {
this.container.style.fontSize = this.settings.fontSize + 'px';
let marquee = this.container.querySelector('.marquee');
if (!marquee) {
marquee = document.createElement('div');
marquee.className = 'marquee';
this.container.appendChild(marquee);
}
this.marquee = marquee;
this.setupMarquee();
}
// Setup the marquee by measuring text, duplicating content, and creating animation
setupMarquee() {
// Clear any existing content in the marquee track
this.marquee.innerHTML = '';
// Create a temporary element to measure the width of one copy of the text
const tempSpan = document.createElement('span');
tempSpan.className = 'marquee-text';
tempSpan.style.visibility = 'hidden';
tempSpan.style.position = 'absolute';
tempSpan.textContent = this.settings.text;
document.body.appendChild(tempSpan);
const textWidth = tempSpan.getBoundingClientRect().width;
document.body.removeChild(tempSpan);
// Get the container width
const containerWidth = this.container.getBoundingClientRect().width;
// Calculate the number of copies needed for a seamless loop
let copies = Math.ceil((containerWidth + textWidth) / textWidth);
copies = Math.max(copies, 2); // Ensure at least two copies
// Append copies of the text into the marquee track
for (let i = 0; i < copies; i++) {
const span = document.createElement('span');
span.className = 'marquee-text';
span.textContent = this.settings.text;
this.marquee.appendChild(span);
}
// Determine the animation duration (in seconds)
const duration = textWidth / this.settings.speed;
// Create a unique animation name (useful for multiple marquees)
const animationName = 'marqueeAnimation_' + Math.random().toString(36).substr(2, 9);
// Create dynamic keyframes based on the direction
let keyframes;
if (this.settings.direction === 'left') {
keyframes = `
@keyframes ${animationName} {
0% { transform: translateX(0); }
100% { transform: translateX(-${textWidth}px); }
}
`;
this.marquee.style.transform = 'translateX(0)';
} else if (this.settings.direction === 'right') {
keyframes = `
@keyframes ${animationName} {
0% { transform: translateX(-${textWidth}px); }
100% { transform: translateX(0); }
}
`;
this.marquee.style.transform = `translateX(-${textWidth}px)`;
}
// Remove any previous dynamic style element if it exists
if (this.animationStyleElement) {
this.animationStyleElement.remove();
}
// Insert the dynamic keyframes into a new style element in the document head
this.animationStyleElement = document.createElement('style');
this.animationStyleElement.innerHTML = keyframes;
document.head.appendChild(this.animationStyleElement);
// Force reflow to ensure the animation starts properly
void this.marquee.offsetWidth;
// Apply the animation to the marquee track
this.marquee.style.animation = `${animationName} ${duration}s linear infinite`;
}
// Update the marquee (for example on window resize)
update() {
this.setupMarquee();
}
}
document.addEventListener('DOMContentLoaded', function() {
// Array to store marquee instances for later updates (e.g., on resize)
const marqueeInstances = [];
// Select all elements with the class "marquee-container"
const containers = document.querySelectorAll('.marquee-container');
containers.forEach(container => {
// Retrieve settings from data attributes, with defaults if not provided
const settings = {
text: container.getAttribute('data-text') || '',
fontSize: parseInt(container.getAttribute('data-font-size')) || 20,
speed: parseFloat(container.getAttribute('data-speed')) || 20,
direction: container.getAttribute('data-direction') || 'left'
};
marqueeInstances.push(new Marquee(container, settings));
});
// On window resize, update all marquees for responsiveness
window.addEventListener('resize', () => {
marqueeInstances.forEach(instance => instance.update());
});
});
</script>