I’ll build this as a standalone advanced marquee chunk for your RX Theme: safe, reusable, accessible, responsive, pauseable, and compatible with WordPress front-end loading.
Yes dear friend. Create this file:
assets/static-js/static-chunks-js/chunk-037-marquee.js
This is an advanced RX Theme marquee chunk. It supports auto marquee, hover pause, focus pause, drag pause, reduced-motion safety, RTL direction, duplicate-track animation, speed control, pause button, accessibility, mutation refresh, and WordPress-friendly initialization.
/*!
* RX Theme - Advanced Marquee Chunk
* File: assets/static-js/static-chunks-js/chunk-037-marquee.js
* Version: 1.0.0
* Author: RX Theme
*
* Usage HTML:
*
* <div class="rx-marquee" data-rx-marquee data-speed="60" data-direction="left" data-pause-on-hover="true">
* <div class="rx-marquee__track">
* <span class="rx-marquee__item">Breaking medical update</span>
* <span class="rx-marquee__item">Health news</span>
* <span class="rx-marquee__item">RX Harun</span>
* </div>
* </div>
*/
(function () {
'use strict';
var RXThemeMarquee = {
version: '1.0.0',
selector: '[data-rx-marquee]',
instances: [],
resizeTimer: null,
mutationObserver: null,
defaults: {
speed: 60,
direction: 'left',
pauseOnHover: true,
pauseOnFocus: true,
pauseOnTouch: true,
pauseOnDrag: true,
pauseOnHidden: true,
duplicate: true,
gap: 32,
minItems: 2,
reverseOnRtl: false,
respectReducedMotion: true,
ariaLive: 'off',
button: false,
buttonTextPause: 'Pause marquee',
buttonTextPlay: 'Play marquee'
}
};
function toBoolean(value, fallback) {
if (value === undefined || value === null || value === '') {
return fallback;
}
if (typeof value === 'boolean') {
return value;
}
value = String(value).toLowerCase().trim();
if (value === 'true' || value === '1' || value === 'yes' || value === 'on') {
return true;
}
if (value === 'false' || value === '0' || value === 'no' || value === 'off') {
return false;
}
return fallback;
}
function toNumber(value, fallback) {
var number = Number(value);
if (Number.isFinite(number)) {
return number;
}
return fallback;
}
function clamp(number, min, max) {
return Math.min(Math.max(number, min), max);
}
function getDatasetOptions(element) {
return {
speed: clamp(toNumber(element.getAttribute('data-speed'), RXThemeMarquee.defaults.speed), 5, 500),
direction: element.getAttribute('data-direction') || RXThemeMarquee.defaults.direction,
pauseOnHover: toBoolean(element.getAttribute('data-pause-on-hover'), RXThemeMarquee.defaults.pauseOnHover),
pauseOnFocus: toBoolean(element.getAttribute('data-pause-on-focus'), RXThemeMarquee.defaults.pauseOnFocus),
pauseOnTouch: toBoolean(element.getAttribute('data-pause-on-touch'), RXThemeMarquee.defaults.pauseOnTouch),
pauseOnDrag: toBoolean(element.getAttribute('data-pause-on-drag'), RXThemeMarquee.defaults.pauseOnDrag),
pauseOnHidden: toBoolean(element.getAttribute('data-pause-on-hidden'), RXThemeMarquee.defaults.pauseOnHidden),
duplicate: toBoolean(element.getAttribute('data-duplicate'), RXThemeMarquee.defaults.duplicate),
gap: clamp(toNumber(element.getAttribute('data-gap'), RXThemeMarquee.defaults.gap), 0, 300),
minItems: clamp(toNumber(element.getAttribute('data-min-items'), RXThemeMarquee.defaults.minItems), 1, 20),
reverseOnRtl: toBoolean(element.getAttribute('data-reverse-on-rtl'), RXThemeMarquee.defaults.reverseOnRtl),
respectReducedMotion: toBoolean(element.getAttribute('data-respect-reduced-motion'), RXThemeMarquee.defaults.respectReducedMotion),
ariaLive: element.getAttribute('data-aria-live') || RXThemeMarquee.defaults.ariaLive,
button: toBoolean(element.getAttribute('data-button'), RXThemeMarquee.defaults.button),
buttonTextPause: element.getAttribute('data-button-text-pause') || RXThemeMarquee.defaults.buttonTextPause,
buttonTextPlay: element.getAttribute('data-button-text-play') || RXThemeMarquee.defaults.buttonTextPlay
};
}
function prefersReducedMotion() {
return window.matchMedia &&
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
}
function isRTL(element) {
var direction = window.getComputedStyle(element).direction;
return direction === 'rtl' || document.documentElement.getAttribute('dir') === 'rtl';
}
function createElement(tag, className, attributes) {
var element = document.createElement(tag);
if (className) {
element.className = className;
}
if (attributes) {
Object.keys(attributes).forEach(function (key) {
element.setAttribute(key, attributes[key]);
});
}
return element;
}
function setStyles(element, styles) {
Object.keys(styles).forEach(function (property) {
element.style[property] = styles[property];
});
}
function getTrack(element) {
return element.querySelector('.rx-marquee__track');
}
function getItems(track) {
return Array.prototype.slice.call(track.children).filter(function (child) {
return !child.hasAttribute('data-rx-clone') && !child.classList.contains('rx-marquee__clone');
});
}
function removeClones(track) {
var clones = track.querySelectorAll('[data-rx-clone="true"], .rx-marquee__clone');
Array.prototype.forEach.call(clones, function (clone) {
clone.parentNode.removeChild(clone);
});
}
function duplicateItems(track, options) {
var originalItems = getItems(track);
if (!originalItems.length) {
return;
}
var cloneFragment = document.createDocumentFragment();
var cloneCount = Math.max(options.minItems, originalItems.length);
for (var i = 0; i < cloneCount; i++) {
var item = originalItems[i % originalItems.length];
var clone = item.cloneNode(true);
clone.setAttribute('aria-hidden', 'true');
clone.setAttribute('data-rx-clone', 'true');
clone.classList.add('rx-marquee__clone');
cloneFragment.appendChild(clone);
}
track.appendChild(cloneFragment);
}
function injectBaseStyles() {
if (document.getElementById('rx-theme-marquee-style')) {
return;
}
var style = document.createElement('style');
style.id = 'rx-theme-marquee-style';
style.textContent = [
'.rx-marquee{',
'position:relative;',
'overflow:hidden;',
'width:100%;',
'--rx-marquee-gap:32px;',
'--rx-marquee-duration:30s;',
'--rx-marquee-distance:-50%;',
'}',
'.rx-marquee__viewport{',
'overflow:hidden;',
'width:100%;',
'}',
'.rx-marquee__track{',
'display:flex;',
'align-items:center;',
'width:max-content;',
'gap:var(--rx-marquee-gap);',
'will-change:transform;',
'animation:rxMarqueeMove var(--rx-marquee-duration) linear infinite;',
'}',
'.rx-marquee.is-paused .rx-marquee__track,',
'.rx-marquee.is-reduced-motion .rx-marquee__track{',
'animation-play-state:paused;',
'}',
'.rx-marquee.is-reduced-motion .rx-marquee__track{',
'transform:none !important;',
'}',
'.rx-marquee__item{',
'flex:0 0 auto;',
'white-space:nowrap;',
'}',
'.rx-marquee__toggle{',
'position:absolute;',
'right:8px;',
'top:50%;',
'transform:translateY(-50%);',
'z-index:2;',
'cursor:pointer;',
'}',
'@keyframes rxMarqueeMove{',
'from{transform:translate3d(0,0,0);}',
'to{transform:translate3d(var(--rx-marquee-distance),0,0);}',
'}'
].join('');
document.head.appendChild(style);
}
function Marquee(element) {
this.element = element;
this.options = getDatasetOptions(element);
this.track = null;
this.viewport = null;
this.button = null;
this.isPaused = false;
this.isDestroyed = false;
this.isPointerDown = false;
this.pointerStartX = 0;
this.pointerCurrentX = 0;
this.dragDistance = 0;
this.visibilityHandler = null;
this.boundHandlers = {};
this.init();
}
Marquee.prototype.init = function () {
if (!this.element || this.element.__rxMarqueeInitialized) {
return;
}
injectBaseStyles();
this.element.__rxMarqueeInitialized = true;
this.element.classList.add('rx-marquee');
this.prepareStructure();
this.prepareAccessibility();
this.setup();
this.bindEvents();
if (this.options.button) {
this.createToggleButton();
}
if (this.options.respectReducedMotion && prefersReducedMotion()) {
this.element.classList.add('is-reduced-motion');
this.pause();
}
this.dispatch('rx-marquee:init');
};
Marquee.prototype.prepareStructure = function () {
this.track = getTrack(this.element);
if (!this.track) {
this.track = createElement('div', 'rx-marquee__track');
while (this.element.firstChild) {
this.track.appendChild(this.element.firstChild);
}
this.element.appendChild(this.track);
}
if (!this.track.parentElement.classList.contains('rx-marquee__viewport')) {
this.viewport = createElement('div', 'rx-marquee__viewport');
this.track.parentNode.insertBefore(this.viewport, this.track);
this.viewport.appendChild(this.track);
} else {
this.viewport = this.track.parentElement;
}
this.track.classList.add('rx-marquee__track');
Array.prototype.forEach.call(this.track.children, function (child) {
if (!child.classList.contains('rx-marquee__item') && !child.hasAttribute('data-rx-clone')) {
child.classList.add('rx-marquee__item');
}
});
};
Marquee.prototype.prepareAccessibility = function () {
this.element.setAttribute('role', this.element.getAttribute('role') || 'region');
this.element.setAttribute('aria-live', this.options.ariaLive);
this.element.setAttribute('data-rx-marquee-ready', 'true');
if (!this.element.getAttribute('aria-label')) {
this.element.setAttribute('aria-label', 'Scrolling content');
}
};
Marquee.prototype.setup = function () {
if (!this.track) {
return;
}
removeClones(this.track);
if (this.options.duplicate) {
duplicateItems(this.track, this.options);
}
this.applyLayout();
};
Marquee.prototype.applyLayout = function () {
var direction = this.options.direction;
var rtl = isRTL(this.element);
if (this.options.reverseOnRtl && rtl) {
if (direction === 'left') {
direction = 'right';
} else if (direction === 'right') {
direction = 'left';
}
}
var distance = '-50%';
if (direction === 'right') {
distance = '50%';
}
var trackWidth = this.track.scrollWidth || 1;
var speed = this.options.speed || RXThemeMarquee.defaults.speed;
var duration = Math.max(trackWidth / speed, 4);
this.element.style.setProperty('--rx-marquee-gap', this.options.gap + 'px');
this.element.style.setProperty('--rx-marquee-duration', duration + 's');
this.element.style.setProperty('--rx-marquee-distance', distance);
setStyles(this.track, {
gap: this.options.gap + 'px'
});
this.element.setAttribute('data-current-direction', direction);
this.element.setAttribute('data-current-speed', String(speed));
};
Marquee.prototype.bindEvents = function () {
var self = this;
this.boundHandlers.mouseenter = function () {
if (self.options.pauseOnHover) {
self.pause();
}
};
this.boundHandlers.mouseleave = function () {
if (self.options.pauseOnHover) {
self.play();
}
};
this.boundHandlers.focusin = function () {
if (self.options.pauseOnFocus) {
self.pause();
}
};
this.boundHandlers.focusout = function () {
if (self.options.pauseOnFocus) {
self.play();
}
};
this.boundHandlers.pointerdown = function (event) {
if (!self.options.pauseOnDrag && !self.options.pauseOnTouch) {
return;
}
self.isPointerDown = true;
self.pointerStartX = event.clientX || 0;
self.pointerCurrentX = self.pointerStartX;
self.dragDistance = 0;
if (self.options.pauseOnTouch || self.options.pauseOnDrag) {
self.pause();
}
};
this.boundHandlers.pointermove = function (event) {
if (!self.isPointerDown) {
return;
}
self.pointerCurrentX = event.clientX || 0;
self.dragDistance = Math.abs(self.pointerCurrentX - self.pointerStartX);
if (self.dragDistance > 8) {
self.element.classList.add('is-dragging');
}
};
this.boundHandlers.pointerup = function () {
if (!self.isPointerDown) {
return;
}
self.isPointerDown = false;
self.element.classList.remove('is-dragging');
if (self.options.pauseOnTouch || self.options.pauseOnDrag) {
self.play();
}
};
this.boundHandlers.resize = function () {
clearTimeout(RXThemeMarquee.resizeTimer);
RXThemeMarquee.resizeTimer = setTimeout(function () {
self.refresh();
}, 150);
};
this.element.addEventListener('mouseenter', this.boundHandlers.mouseenter);
this.element.addEventListener('mouseleave', this.boundHandlers.mouseleave);
this.element.addEventListener('focusin', this.boundHandlers.focusin);
this.element.addEventListener('focusout', this.boundHandlers.focusout);
this.element.addEventListener('pointerdown', this.boundHandlers.pointerdown);
window.addEventListener('pointermove', this.boundHandlers.pointermove);
window.addEventListener('pointerup', this.boundHandlers.pointerup);
window.addEventListener('resize', this.boundHandlers.resize);
if (this.options.pauseOnHidden) {
this.visibilityHandler = function () {
if (document.hidden) {
self.pause();
} else {
self.play();
}
};
document.addEventListener('visibilitychange', this.visibilityHandler);
}
};
Marquee.prototype.createToggleButton = function () {
var self = this;
if (this.button) {
return;
}
this.button = createElement('button', 'rx-marquee__toggle', {
type: 'button',
'aria-pressed': 'false',
'aria-label': this.options.buttonTextPause
});
this.button.textContent = '❚❚';
this.button.addEventListener('click', function () {
if (self.isPaused) {
self.play();
} else {
self.pause();
}
});
this.element.appendChild(this.button);
};
Marquee.prototype.updateButton = function () {
if (!this.button) {
return;
}
if (this.isPaused) {
this.button.setAttribute('aria-pressed', 'true');
this.button.setAttribute('aria-label', this.options.buttonTextPlay);
this.button.textContent = '▶';
} else {
this.button.setAttribute('aria-pressed', 'false');
this.button.setAttribute('aria-label', this.options.buttonTextPause);
this.button.textContent = '❚❚';
}
};
Marquee.prototype.pause = function () {
if (this.isDestroyed) {
return;
}
this.isPaused = true;
this.element.classList.add('is-paused');
this.updateButton();
this.dispatch('rx-marquee:pause');
};
Marquee.prototype.play = function () {
if (this.isDestroyed) {
return;
}
if (this.options.respectReducedMotion && prefersReducedMotion()) {
this.pause();
return;
}
this.isPaused = false;
this.element.classList.remove('is-paused');
this.updateButton();
this.dispatch('rx-marquee:play');
};
Marquee.prototype.refresh = function () {
if (this.isDestroyed) {
return;
}
this.options = getDatasetOptions(this.element);
this.setup();
if (this.isPaused) {
this.pause();
} else {
this.play();
}
this.dispatch('rx-marquee:refresh');
};
Marquee.prototype.setSpeed = function (speed) {
speed = clamp(toNumber(speed, this.options.speed), 5, 500);
this.options.speed = speed;
this.element.setAttribute('data-speed', String(speed));
this.applyLayout();
this.dispatch('rx-marquee:speed');
};
Marquee.prototype.setDirection = function (direction) {
if (['left', 'right'].indexOf(direction) === -1) {
return;
}
this.options.direction = direction;
this.element.setAttribute('data-direction', direction);
this.applyLayout();
this.dispatch('rx-marquee:direction');
};
Marquee.prototype.destroy = function () {
if (this.isDestroyed) {
return;
}
this.isDestroyed = true;
removeClones(this.track);
this.element.removeEventListener('mouseenter', this.boundHandlers.mouseenter);
this.element.removeEventListener('mouseleave', this.boundHandlers.mouseleave);
this.element.removeEventListener('focusin', this.boundHandlers.focusin);
this.element.removeEventListener('focusout', this.boundHandlers.focusout);
this.element.removeEventListener('pointerdown', this.boundHandlers.pointerdown);
window.removeEventListener('pointermove', this.boundHandlers.pointermove);
window.removeEventListener('pointerup', this.boundHandlers.pointerup);
window.removeEventListener('resize', this.boundHandlers.resize);
if (this.visibilityHandler) {
document.removeEventListener('visibilitychange', this.visibilityHandler);
}
if (this.button && this.button.parentNode) {
this.button.parentNode.removeChild(this.button);
}
this.element.classList.remove('is-paused', 'is-dragging', 'is-reduced-motion');
this.element.removeAttribute('data-rx-marquee-ready');
this.element.__rxMarqueeInitialized = false;
this.dispatch('rx-marquee:destroy');
};
Marquee.prototype.dispatch = function (name) {
var event;
try {
event = new CustomEvent(name, {
bubbles: true,
detail: {
instance: this,
element: this.element,
options: this.options
}
});
} catch (error) {
event = document.createEvent('CustomEvent');
event.initCustomEvent(name, true, false, {
instance: this,
element: this.element,
options: this.options
});
}
this.element.dispatchEvent(event);
};
RXThemeMarquee.init = function (context) {
context = context || document;
var elements = context.querySelectorAll
? context.querySelectorAll(RXThemeMarquee.selector)
: [];
Array.prototype.forEach.call(elements, function (element) {
if (!element.__rxMarqueeInitialized) {
var instance = new Marquee(element);
RXThemeMarquee.instances.push(instance);
}
});
RXThemeMarquee.observeNewMarquees();
};
RXThemeMarquee.refresh = function () {
RXThemeMarquee.instances.forEach(function (instance) {
if (instance && !instance.isDestroyed) {
instance.refresh();
}
});
};
RXThemeMarquee.pauseAll = function () {
RXThemeMarquee.instances.forEach(function (instance) {
if (instance && !instance.isDestroyed) {
instance.pause();
}
});
};
RXThemeMarquee.playAll = function () {
RXThemeMarquee.instances.forEach(function (instance) {
if (instance && !instance.isDestroyed) {
instance.play();
}
});
};
RXThemeMarquee.destroyAll = function () {
RXThemeMarquee.instances.forEach(function (instance) {
if (instance && !instance.isDestroyed) {
instance.destroy();
}
});
RXThemeMarquee.instances = [];
};
RXThemeMarquee.getInstance = function (element) {
for (var i = 0; i < RXThemeMarquee.instances.length; i++) {
if (RXThemeMarquee.instances[i].element === element) {
return RXThemeMarquee.instances[i];
}
}
return null;
};
RXThemeMarquee.observeNewMarquees = function () {
if (RXThemeMarquee.mutationObserver || !window.MutationObserver) {
return;
}
RXThemeMarquee.mutationObserver = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
Array.prototype.forEach.call(mutation.addedNodes, function (node) {
if (!node || node.nodeType !== 1) {
return;
}
if (node.matches && node.matches(RXThemeMarquee.selector)) {
RXThemeMarquee.init(node.parentNode || document);
return;
}
if (node.querySelector && node.querySelector(RXThemeMarquee.selector)) {
RXThemeMarquee.init(node);
}
});
});
});
RXThemeMarquee.mutationObserver.observe(document.documentElement, {
childList: true,
subtree: true
});
};
function domReady(callback) {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', callback);
} else {
callback();
}
}
domReady(function () {
RXThemeMarquee.init(document);
});
window.RXThemeMarquee = RXThemeMarquee;
})();
Example HTML
<div
class="rx-marquee"
data-rx-marquee
data-speed="70"
data-direction="left"
data-gap="40"
data-pause-on-hover="true"
data-pause-on-focus="true"
data-button="true"
aria-label="Latest RX medical updates"
>
<div class="rx-marquee__track">
<span class="rx-marquee__item">Latest medical articles</span>
<span class="rx-marquee__item">Evidence based health knowledge</span>
<span class="rx-marquee__item">A global war against illness</span>
<span class="rx-marquee__item">RX Harun medical website</span>
</div>
</div>
Enqueue in functions.php
function rx_theme_enqueue_static_chunks() {
wp_enqueue_script(
'rx-chunk-037-marquee',
get_template_directory_uri() . '/assets/static-js/static-chunks-js/chunk-037-marquee.js',
array(),
'1.0.0',
true
);
}
add_action( 'wp_enqueue_scripts', 'rx_theme_enqueue_static_chunks' );
This file is safe as a static chunk JS because it only runs when it finds:
data-rx-marquee
So it will not affect other parts of your RX Theme.