I’ll build this as a strong standalone breadcrumb chunk file for RX Theme: safe, dependency-light, performance-friendly, SEO-aware, and compatible with WordPress-rendered breadcrumb HTML.
Yes dear friend — for this file:
assets/static-js/static-chunks-js/chunk-022-breadcrumb.js
Use it for advanced breadcrumb UI behavior, not for generating the main SEO breadcrumb schema. The real breadcrumb HTML + JSON-LD should come from PHP/WordPress, but this JavaScript can improve UX, accessibility, mobile behavior, scroll behavior, copy/share, active state, and safety.
Copy this full code:
/*!
* RX Theme - Breadcrumb Advanced Chunk
* File: assets/static-js/static-chunks-js/chunk-022-breadcrumb.js
* Purpose: Advanced breadcrumb enhancement for UX, accessibility, mobile, SEO-safe UI behavior.
* Author: RxHarun
*/
(function () {
'use strict';
/**
* ============================================================
* RX Breadcrumb Advanced Controller
* ============================================================
*
* Recommended HTML structure:
*
* <nav class="rx-breadcrumb" aria-label="Breadcrumb">
* <ol class="rx-breadcrumb__list">
* <li class="rx-breadcrumb__item">
* <a href="/">Home</a>
* </li>
* <li class="rx-breadcrumb__item">
* <a href="/category/">Category</a>
* </li>
* <li class="rx-breadcrumb__item" aria-current="page">
* Current Page
* </li>
* </ol>
* </nav>
*
* Optional data attributes:
*
* data-rx-breadcrumb
* data-rx-scroll="true"
* data-rx-collapse="true"
* data-rx-copy="true"
* data-rx-current-title="Page Title"
*/
var RXBreadcrumb = {
version: '1.0.0',
selectors: {
root: '.rx-breadcrumb, [data-rx-breadcrumb]',
list: '.rx-breadcrumb__list, ol, ul',
item: '.rx-breadcrumb__item, li',
link: 'a[href]',
current: '[aria-current="page"]'
},
classes: {
enhanced: 'rx-breadcrumb--enhanced',
ready: 'rx-breadcrumb--ready',
scrollable: 'rx-breadcrumb--scrollable',
collapsed: 'rx-breadcrumb--collapsed',
expanded: 'rx-breadcrumb--expanded',
overflowStart: 'rx-breadcrumb--overflow-start',
overflowEnd: 'rx-breadcrumb--overflow-end',
current: 'rx-breadcrumb__item--current',
home: 'rx-breadcrumb__item--home',
parent: 'rx-breadcrumb__item--parent',
copied: 'rx-breadcrumb--copied',
noLinks: 'rx-breadcrumb--no-links',
single: 'rx-breadcrumb--single',
long: 'rx-breadcrumb--long',
mobile: 'rx-breadcrumb--mobile'
},
options: {
mobileBreakpoint: 768,
longLabelLength: 42,
collapseMinItems: 5,
maxMiddleItemsMobile: 2,
scrollToEndOnLoad: true,
enableKeyboardScroll: true,
enableCopyPath: true,
enableTitleTooltips: true,
enableCurrentState: true,
enableOverflowShadow: true,
enableSafeExternalRel: true,
enableHomeDetection: true,
enableMobileCollapse: true,
enableAnalyticsEvents: true,
enableJsonLdCheck: true,
debug: false
},
state: {
roots: [],
resizeTimer: null,
mutationObserver: null,
initialized: false
},
init: function () {
if (this.state.initialized) {
return;
}
var roots = document.querySelectorAll(this.selectors.root);
if (!roots.length) {
return;
}
this.state.roots = Array.prototype.slice.call(roots);
for (var i = 0; i < this.state.roots.length; i++) {
this.enhance(this.state.roots[i], i);
}
this.bindGlobalEvents();
this.observeDynamicChanges();
this.state.initialized = true;
this.log('RX Breadcrumb initialized.');
},
enhance: function (root, index) {
if (!root || root.dataset.rxBreadcrumbEnhanced === 'true') {
return;
}
root.dataset.rxBreadcrumbEnhanced = 'true';
root.dataset.rxBreadcrumbIndex = String(index);
this.addBaseAttributes(root);
this.addBaseClasses(root);
this.normalizeStructure(root);
this.enhanceItems(root);
this.markCurrentItem(root);
this.detectHomeItem(root);
this.detectLongLabels(root);
this.detectSingleBreadcrumb(root);
this.addSafeLinkAttributes(root);
this.addTooltips(root);
this.addMobileFeatures(root);
this.addCopyButton(root);
this.bindRootEvents(root);
this.updateOverflowState(root);
this.scrollToCurrent(root);
this.checkJsonLd(root);
root.classList.add(this.classes.ready);
},
addBaseAttributes: function (root) {
if (!root.hasAttribute('aria-label')) {
root.setAttribute('aria-label', 'Breadcrumb');
}
if (!root.hasAttribute('role') && root.tagName.toLowerCase() !== 'nav') {
root.setAttribute('role', 'navigation');
}
},
addBaseClasses: function (root) {
root.classList.add(this.classes.enhanced);
},
normalizeStructure: function (root) {
var list = this.getList(root);
if (!list) {
return;
}
if (!list.hasAttribute('role')) {
list.setAttribute('role', 'list');
}
var items = this.getItems(root);
for (var i = 0; i < items.length; i++) {
if (!items[i].hasAttribute('role')) {
items[i].setAttribute('role', 'listitem');
}
}
},
enhanceItems: function (root) {
var items = this.getItems(root);
for (var i = 0; i < items.length; i++) {
var item = items[i];
item.dataset.rxBreadcrumbPosition = String(i + 1);
if (i < items.length - 1) {
item.classList.add(this.classes.parent);
}
var link = item.querySelector(this.selectors.link);
if (link) {
this.cleanLinkText(link);
this.addLinkTracking(link, i, items.length);
}
}
},
cleanLinkText: function (link) {
if (!link) {
return;
}
var text = link.textContent || '';
text = text.replace(/\s+/g, ' ').trim();
if (text) {
link.textContent = text;
}
},
markCurrentItem: function (root) {
if (!this.options.enableCurrentState) {
return;
}
var items = this.getItems(root);
if (!items.length) {
return;
}
var current = root.querySelector(this.selectors.current);
if (!current) {
current = items[items.length - 1];
current.setAttribute('aria-current', 'page');
}
current.classList.add(this.classes.current);
var currentLink = current.matches('a') ? current : current.querySelector('a');
if (currentLink) {
currentLink.setAttribute('aria-current', 'page');
}
},
detectHomeItem: function (root) {
if (!this.options.enableHomeDetection) {
return;
}
var items = this.getItems(root);
if (!items.length) {
return;
}
var first = items[0];
var text = (first.textContent || '').trim().toLowerCase();
if (
text === 'home' ||
text === 'homepage' ||
text === 'start' ||
text === 'rxharun' ||
text === 'rx harun'
) {
first.classList.add(this.classes.home);
}
},
detectLongLabels: function (root) {
var items = this.getItems(root);
var hasLong = false;
for (var i = 0; i < items.length; i++) {
var text = (items[i].textContent || '').trim();
if (text.length > this.options.longLabelLength) {
hasLong = true;
items[i].classList.add('rx-breadcrumb__item--long');
}
}
if (hasLong) {
root.classList.add(this.classes.long);
}
},
detectSingleBreadcrumb: function (root) {
var items = this.getItems(root);
if (items.length <= 1) {
root.classList.add(this.classes.single);
}
var links = root.querySelectorAll(this.selectors.link);
if (!links.length) {
root.classList.add(this.classes.noLinks);
}
},
addSafeLinkAttributes: function (root) {
if (!this.options.enableSafeExternalRel) {
return;
}
var links = root.querySelectorAll(this.selectors.link);
for (var i = 0; i < links.length; i++) {
var link = links[i];
if (this.isExternalLink(link)) {
var rel = link.getAttribute('rel') || '';
var relParts = rel.split(/\s+/);
if (relParts.indexOf('noopener') === -1) {
relParts.push('noopener');
}
if (relParts.indexOf('noreferrer') === -1) {
relParts.push('noreferrer');
}
link.setAttribute('rel', relParts.join(' ').trim());
}
}
},
addTooltips: function (root) {
if (!this.options.enableTitleTooltips) {
return;
}
var items = this.getItems(root);
for (var i = 0; i < items.length; i++) {
var item = items[i];
var link = item.querySelector(this.selectors.link);
var target = link || item;
var text = (target.textContent || '').replace(/\s+/g, ' ').trim();
if (text && !target.hasAttribute('title')) {
target.setAttribute('title', text);
}
}
},
addMobileFeatures: function (root) {
if (!this.options.enableMobileCollapse) {
return;
}
var items = this.getItems(root);
if (items.length < this.options.collapseMinItems) {
return;
}
root.dataset.rxCanCollapse = 'true';
var button = document.createElement('button');
button.type = 'button';
button.className = 'rx-breadcrumb__toggle';
button.setAttribute('aria-expanded', 'false');
button.setAttribute('aria-label', 'Show full breadcrumb path');
button.innerHTML = '<span aria-hidden="true">…</span>';
var list = this.getList(root);
if (list && !root.querySelector('.rx-breadcrumb__toggle')) {
root.insertBefore(button, list);
}
this.applyCollapse(root);
},
applyCollapse: function (root) {
if (!root || root.dataset.rxCanCollapse !== 'true') {
return;
}
var isMobile = window.innerWidth <= this.options.mobileBreakpoint;
var items = this.getItems(root);
var toggle = root.querySelector('.rx-breadcrumb__toggle');
root.classList.toggle(this.classes.mobile, isMobile);
if (!isMobile) {
root.classList.remove(this.classes.collapsed);
root.classList.add(this.classes.expanded);
for (var i = 0; i < items.length; i++) {
items[i].hidden = false;
}
if (toggle) {
toggle.hidden = true;
toggle.setAttribute('aria-expanded', 'true');
}
return;
}
if (root.classList.contains(this.classes.expanded)) {
for (var a = 0; a < items.length; a++) {
items[a].hidden = false;
}
if (toggle) {
toggle.hidden = false;
toggle.setAttribute('aria-expanded', 'true');
}
return;
}
root.classList.add(this.classes.collapsed);
root.classList.remove(this.classes.expanded);
for (var j = 0; j < items.length; j++) {
var shouldShow =
j === 0 ||
j >= items.length - this.options.maxMiddleItemsMobile;
items[j].hidden = !shouldShow;
}
if (toggle) {
toggle.hidden = false;
toggle.setAttribute('aria-expanded', 'false');
}
},
addCopyButton: function (root) {
if (!this.options.enableCopyPath) {
return;
}
var allowCopy = root.dataset.rxCopy;
if (allowCopy === 'false') {
return;
}
if (root.querySelector('.rx-breadcrumb__copy')) {
return;
}
var button = document.createElement('button');
button.type = 'button';
button.className = 'rx-breadcrumb__copy';
button.setAttribute('aria-label', 'Copy breadcrumb path');
button.textContent = 'Copy path';
root.appendChild(button);
},
bindRootEvents: function (root) {
var self = this;
root.addEventListener('click', function (event) {
var toggle = event.target.closest('.rx-breadcrumb__toggle');
var copy = event.target.closest('.rx-breadcrumb__copy');
var link = event.target.closest(self.selectors.link);
if (toggle && root.contains(toggle)) {
self.toggleCollapse(root, toggle);
return;
}
if (copy && root.contains(copy)) {
self.copyBreadcrumbPath(root);
return;
}
if (link && root.contains(link)) {
self.dispatchAnalyticsEvent(root, link);
}
});
if (this.options.enableKeyboardScroll) {
root.addEventListener('keydown', function (event) {
self.handleKeyboard(root, event);
});
}
var list = this.getList(root);
if (list) {
list.addEventListener('scroll', function () {
self.updateOverflowState(root);
});
}
},
toggleCollapse: function (root, toggle) {
var expanded = toggle.getAttribute('aria-expanded') === 'true';
var items = this.getItems(root);
if (expanded) {
root.classList.remove(this.classes.expanded);
root.classList.add(this.classes.collapsed);
toggle.setAttribute('aria-expanded', 'false');
toggle.setAttribute('aria-label', 'Show full breadcrumb path');
for (var i = 0; i < items.length; i++) {
var shouldShow =
i === 0 ||
i >= items.length - this.options.maxMiddleItemsMobile;
items[i].hidden = !shouldShow;
}
} else {
root.classList.remove(this.classes.collapsed);
root.classList.add(this.classes.expanded);
toggle.setAttribute('aria-expanded', 'true');
toggle.setAttribute('aria-label', 'Hide full breadcrumb path');
for (var j = 0; j < items.length; j++) {
items[j].hidden = false;
}
}
},
copyBreadcrumbPath: function (root) {
var path = this.getBreadcrumbText(root);
if (!path) {
return;
}
var self = this;
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(path).then(function () {
self.showCopiedState(root);
}).catch(function () {
self.fallbackCopy(path, root);
});
} else {
this.fallbackCopy(path, root);
}
},
fallbackCopy: function (text, root) {
var textarea = document.createElement('textarea');
textarea.value = text;
textarea.setAttribute('readonly', 'readonly');
textarea.style.position = 'fixed';
textarea.style.top = '-9999px';
textarea.style.left = '-9999px';
document.body.appendChild(textarea);
textarea.select();
try {
document.execCommand('copy');
this.showCopiedState(root);
} catch (error) {
this.log('Copy failed.', error);
}
document.body.removeChild(textarea);
},
showCopiedState: function (root) {
var button = root.querySelector('.rx-breadcrumb__copy');
root.classList.add(this.classes.copied);
if (button) {
var oldText = button.textContent;
button.textContent = 'Copied';
button.setAttribute('aria-label', 'Breadcrumb path copied');
window.setTimeout(function () {
button.textContent = oldText;
button.setAttribute('aria-label', 'Copy breadcrumb path');
root.classList.remove('rx-breadcrumb--copied');
}, 1600);
}
},
handleKeyboard: function (root, event) {
var list = this.getList(root);
if (!list) {
return;
}
var amount = 80;
if (event.key === 'ArrowLeft') {
list.scrollLeft -= amount;
}
if (event.key === 'ArrowRight') {
list.scrollLeft += amount;
}
if (event.key === 'Home') {
list.scrollLeft = 0;
}
if (event.key === 'End') {
list.scrollLeft = list.scrollWidth;
}
this.updateOverflowState(root);
},
updateOverflowState: function (root) {
if (!this.options.enableOverflowShadow) {
return;
}
var list = this.getList(root);
if (!list) {
return;
}
var hasOverflow = list.scrollWidth > list.clientWidth + 2;
var atStart = list.scrollLeft <= 2;
var atEnd = list.scrollLeft + list.clientWidth >= list.scrollWidth - 2;
root.classList.toggle(this.classes.scrollable, hasOverflow);
root.classList.toggle(this.classes.overflowStart, hasOverflow && !atStart);
root.classList.toggle(this.classes.overflowEnd, hasOverflow && !atEnd);
},
scrollToCurrent: function (root) {
if (!this.options.scrollToEndOnLoad) {
return;
}
var list = this.getList(root);
if (!list) {
return;
}
window.requestAnimationFrame(function () {
list.scrollLeft = list.scrollWidth;
});
},
checkJsonLd: function (root) {
if (!this.options.enableJsonLdCheck) {
return;
}
var scripts = document.querySelectorAll('script[type="application/ld+json"]');
var hasBreadcrumbSchema = false;
for (var i = 0; i < scripts.length; i++) {
var text = scripts[i].textContent || '';
if (
text.indexOf('BreadcrumbList') !== -1 ||
text.indexOf('"@type":"BreadcrumbList"') !== -1 ||
text.indexOf('"@type": "BreadcrumbList"') !== -1
) {
hasBreadcrumbSchema = true;
break;
}
}
root.dataset.rxHasBreadcrumbSchema = hasBreadcrumbSchema ? 'true' : 'false';
},
dispatchAnalyticsEvent: function (root, link) {
if (!this.options.enableAnalyticsEvents) {
return;
}
var eventDetail = {
component: 'breadcrumb',
text: (link.textContent || '').trim(),
href: link.href,
position: link.closest(this.selectors.item)
? link.closest(this.selectors.item).dataset.rxBreadcrumbPosition
: null
};
var customEvent;
try {
customEvent = new CustomEvent('rx:breadcrumb:click', {
bubbles: true,
detail: eventDetail
});
} catch (error) {
customEvent = document.createEvent('CustomEvent');
customEvent.initCustomEvent('rx:breadcrumb:click', true, false, eventDetail);
}
root.dispatchEvent(customEvent);
if (window.dataLayer && Array.isArray(window.dataLayer)) {
window.dataLayer.push({
event: 'rx_breadcrumb_click',
breadcrumb_text: eventDetail.text,
breadcrumb_href: eventDetail.href,
breadcrumb_position: eventDetail.position
});
}
},
addLinkTracking: function (link, index, total) {
link.dataset.rxBreadcrumbLink = 'true';
link.dataset.rxBreadcrumbIndex = String(index + 1);
link.dataset.rxBreadcrumbTotal = String(total);
},
bindGlobalEvents: function () {
var self = this;
window.addEventListener('resize', function () {
window.clearTimeout(self.state.resizeTimer);
self.state.resizeTimer = window.setTimeout(function () {
self.refreshAll();
}, 150);
});
window.addEventListener('orientationchange', function () {
window.setTimeout(function () {
self.refreshAll();
}, 250);
});
},
observeDynamicChanges: function () {
if (!window.MutationObserver) {
return;
}
var self = this;
this.state.mutationObserver = new MutationObserver(function (mutations) {
var shouldRefresh = false;
for (var i = 0; i < mutations.length; i++) {
var mutation = mutations[i];
if (mutation.addedNodes && mutation.addedNodes.length) {
for (var j = 0; j < mutation.addedNodes.length; j++) {
var node = mutation.addedNodes[j];
if (node.nodeType !== 1) {
continue;
}
if (
node.matches &&
(
node.matches(self.selectors.root) ||
node.querySelector(self.selectors.root)
)
) {
shouldRefresh = true;
break;
}
}
}
if (shouldRefresh) {
break;
}
}
if (shouldRefresh) {
self.refreshNew();
}
});
this.state.mutationObserver.observe(document.documentElement, {
childList: true,
subtree: true
});
},
refreshAll: function () {
var roots = document.querySelectorAll(this.selectors.root);
for (var i = 0; i < roots.length; i++) {
this.applyCollapse(roots[i]);
this.updateOverflowState(roots[i]);
}
},
refreshNew: function () {
var roots = document.querySelectorAll(this.selectors.root);
for (var i = 0; i < roots.length; i++) {
if (roots[i].dataset.rxBreadcrumbEnhanced !== 'true') {
this.enhance(roots[i], i);
}
}
},
getBreadcrumbText: function (root) {
var items = this.getItems(root);
var parts = [];
for (var i = 0; i < items.length; i++) {
var text = (items[i].textContent || '')
.replace(/\s+/g, ' ')
.replace('Copy path', '')
.replace('Copied', '')
.trim();
if (text && text !== '…') {
parts.push(text);
}
}
return parts.join(' > ');
},
getList: function (root) {
return root.querySelector(this.selectors.list);
},
getItems: function (root) {
var list = this.getList(root);
if (!list) {
return [];
}
return Array.prototype.slice.call(list.querySelectorAll(this.selectors.item));
},
isExternalLink: function (link) {
if (!link || !link.href) {
return false;
}
try {
return new URL(link.href, window.location.href).origin !== window.location.origin;
} catch (error) {
return false;
}
},
log: function () {
if (!this.options.debug || !window.console) {
return;
}
console.log.apply(console, arguments);
}
};
/**
* Public API
*/
window.RXBreadcrumb = RXBreadcrumb;
/**
* DOM Ready Init
*/
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function () {
RXBreadcrumb.init();
});
} else {
RXBreadcrumb.init();
}
})();
Add this CSS also, otherwise the advanced classes/buttons will work but not look good:
/* RX Theme Breadcrumb Advanced CSS */
.rx-breadcrumb {
position: relative;
width: 100%;
font-size: 14px;
line-height: 1.5;
}
.rx-breadcrumb__list {
display: flex;
align-items: center;
gap: 6px;
padding: 0;
margin: 0;
list-style: none;
overflow-x: auto;
overflow-y: hidden;
scroll-behavior: smooth;
scrollbar-width: thin;
-webkit-overflow-scrolling: touch;
}
.rx-breadcrumb__item {
display: inline-flex;
align-items: center;
flex: 0 0 auto;
max-width: 280px;
white-space: nowrap;
}
.rx-breadcrumb__item:not(:last-child)::after {
content: "/";
margin-left: 6px;
opacity: 0.55;
}
.rx-breadcrumb__item a {
color: inherit;
text-decoration: none;
}
.rx-breadcrumb__item a:hover,
.rx-breadcrumb__item a:focus {
text-decoration: underline;
}
.rx-breadcrumb__item--current {
font-weight: 600;
opacity: 0.85;
}
.rx-breadcrumb__item--long {
overflow: hidden;
text-overflow: ellipsis;
}
.rx-breadcrumb__toggle,
.rx-breadcrumb__copy {
border: 1px solid currentColor;
background: transparent;
color: inherit;
border-radius: 999px;
padding: 2px 8px;
font-size: 12px;
line-height: 1.4;
cursor: pointer;
}
.rx-breadcrumb__toggle:hover,
.rx-breadcrumb__copy:hover {
opacity: 0.8;
}
.rx-breadcrumb__copy {
margin-top: 6px;
}
.rx-breadcrumb--scrollable::before,
.rx-breadcrumb--scrollable::after {
content: "";
position: absolute;
top: 0;
width: 24px;
height: 100%;
pointer-events: none;
z-index: 1;
}
.rx-breadcrumb--overflow-start::before {
left: 0;
background: linear-gradient(to right, currentColor, transparent);
opacity: 0.08;
}
.rx-breadcrumb--overflow-end::after {
right: 0;
background: linear-gradient(to left, currentColor, transparent);
opacity: 0.08;
}
.rx-breadcrumb--copied .rx-breadcrumb__copy {
font-weight: 600;
}
@media (max-width: 768px) {
.rx-breadcrumb {
font-size: 13px;
}
.rx-breadcrumb__item {
max-width: 190px;
}
.rx-breadcrumb__copy {
display: none;
}
}
Then enqueue this file in your RX Theme, for example:
wp_enqueue_script(
'rx-breadcrumb',
get_template_directory_uri() . '/assets/static-js/static-chunks-js/chunk-022-breadcrumb.js',
array(),
filemtime(get_template_directory() . '/assets/static-js/static-chunks-js/chunk-022-breadcrumb.js'),
true
);
Best HTML/PHP breadcrumb output should look like this:
<nav class="rx-breadcrumb" aria-label="Breadcrumb" data-rx-breadcrumb data-rx-collapse="true" data-rx-copy="true">
<ol class="rx-breadcrumb__list">
<li class="rx-breadcrumb__item">
<a href="/">Home</a>
</li>
<li class="rx-breadcrumb__item">
<a href="/diseases/">Diseases</a>
</li>
<li class="rx-breadcrumb__item" aria-current="page">
Current Article Title
</li>
</ol>
</nav>
Important: this JavaScript should enhance breadcrumb behavior only. For Google SEO, create breadcrumb links and BreadcrumbList JSON-LD from PHP, because Google reads server-rendered structured data more reliably than JavaScript-only breadcrumb generation.