/**
* Submenu viewport edge detection.
*
* Measures each dropdown/flyout before it opens and flips it leftward only if
* it would extend past the right edge of the viewport. Works on hover and on
* keyboard focus. No jQuery dependency, no theme class names hardcoded.
*
* Install: paste into Jupiter X > Settings > Custom JS, or enqueue as a file
* from a child theme. If pasting into a Custom JS field, omit nothing —
* the script guards its own initialization.
*/
(function () {
‘use strict’;
// Adjust this selector if it grabs the wrong menu. It should match the
// top-level
- of your main navigation.
var MENU_SELECTOR = ‘header nav ul, .jupiterx-nav-menu, #menu-main, .menu’;
var EDGE_MARGIN = 12; // px of breathing room to keep at the viewport edge
var MIN_WIDTH = 1025; // below this, the mobile menu takes over — skip
var STYLES =
‘.hcpfc-flip-down > ul { left: auto !important; right: 0 !important; }’ +
‘.hcpfc-flip-side > ul { left: auto !important; right: 100% !important; }’;
function injectStyles() {
if (document.getElementById(‘hcpfc-menu-flip-styles’)) return;
var s = document.createElement(‘style’);
s.id = ‘hcpfc-menu-flip-styles’;
s.textContent = STYLES;
document.head.appendChild(s);
}
/**
* Submenus are usually display:none until open, which makes them
* unmeasurable. Briefly force them visible-but-transparent to read a width,
* then restore whatever inline style was there before.
*/
function measureWidth(el) {
if (el.offsetWidth) return el.offsetWidth;
var prev = el.getAttribute(‘style’);
el.style.setProperty(‘display’, ‘block’, ‘important’);
el.style.setProperty(‘visibility’, ‘hidden’, ‘important’);
el.style.setProperty(‘opacity’, ‘0’, ‘important’);
var w = el.offsetWidth;
if (prev === null) el.removeAttribute(‘style’);
else el.setAttribute(‘style’, prev);
return w;
}
function childSubmenu(li) {
for (var i = 0; i < li.children.length; i++) {
if (li.children[i].tagName === 'UL') return li.children[i];
}
return null;
}
function evaluate(li, isTopLevel) {
li.classList.remove('hcpfc-flip-down', 'hcpfc-flip-side');
var sub = childSubmenu(li);
if (!sub) return;
var width = measureWidth(sub);
if (!width) return;
var predictedRight;
if (isTopLevel) {
// Dropdown opens below, aligned to the item's left edge.
predictedRight = li.getBoundingClientRect().left + width;
} else {
// Flyout opens to the right of the submenu that contains this item.
predictedRight = li.parentNode.getBoundingClientRect().right + width;
}
if (predictedRight > window.innerWidth – EDGE_MARGIN) {
li.classList.add(isTopLevel ? ‘hcpfc-flip-down’ : ‘hcpfc-flip-side’);
}
}
function bind(root) {
var items = root.querySelectorAll(‘li’);
for (var i = 0; i < items.length; i++) {
(function (li) {
if (!childSubmenu(li)) return;
var isTopLevel = li.parentNode === root;
var handler = function () {
// Let the theme's own positioning settle first.
window.requestAnimationFrame(function () {
evaluate(li, isTopLevel);
});
};
li.addEventListener('mouseenter', handler);
li.addEventListener('focusin', handler);
})(items[i]);
}
}
function init() {
if (window.innerWidth < MIN_WIDTH) return;
var menus = document.querySelectorAll(MENU_SELECTOR);
if (!menus.length) return;
injectStyles();
for (var i = 0; i < menus.length; i++) {
// Only bind the outermost list of each menu.
if (!menus[i].closest('li')) bind(menus[i]);
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
// Re-evaluate after a resize, since flip decisions are width dependent.
var resizeTimer;
window.addEventListener('resize', function () {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(function () {
var flipped = document.querySelectorAll('.hcpfc-flip-down, .hcpfc-flip-side');
for (var i = 0; i < flipped.length; i++) {
flipped[i].classList.remove('hcpfc-flip-down', 'hcpfc-flip-side');
}
}, 150);
});
})();