根据可用空间自动切换下拉子菜单展开方向
Got it, let's tackle this dropdown overflow issue! The core problem here is that your .sub-menu-list-right is hardcoded to expand to the right (left: 100%), which causes it to go off-screen when the parent menu item is near the window's right edge. We'll fix this by dynamically checking available space and switching the submenu's direction as needed.
Step 1: Update Your CSS
First, let's add a CSS class to handle the left-expanding state. Keep your existing styles, but add this to support both directions:
/* Default right-expanding submenu */ .sub-menu-list-right { top: 0; left: 100%; /* Keep your existing styles (background, z-index, padding, etc.) */ } /* New class for left-expanding submenu */ .sub-menu-list-right.sub-menu-left { left: auto; right: 100%; }
This gives us a toggleable class to flip the submenu's direction without rewriting all existing styles.
Step 2: Add JavaScript to Detect Space and Adjust Position
We need to check the available space on the right side of the parent menu item when the user hovers over it. Here's how to integrate this into your existing JS code:
(function () { handleMenuItems(); handleSubMenuPosition(); // Add this new function call // Your existing handleMenuItems function remains unchanged... function handleMenuItems() { var menuEl = document.querySelector(".menu"), userLinksList = menuEl && menuEl.querySelector(".user-links"), recentlyItemListEl = menuEl && menuEl.querySelector(".recently-visited-item"), favoriteItemListEl = menuEl && menuEl.querySelector(".favorites-item"); if (userLinksList) { userLinksList.addEventListener("mouseover", function (evt) { var options = { url: "/api/userlinks", method: "GET", successCallback: onloadUserLinks }; function onloadUserLinks(result) { if (!window.__RAPMD__) { window.__RAPMD__ = {}; } window.__RAPMD__.lastUserLinks = result; createMenuList(result.RecentLinks, recentlyItemListEl, "No recently visited pages"); createMenuList(result.Favorites, favoriteItemListEl, "No favorite pages"); } if (!window.__RAPMD__ || !window.__RAPMD__.lastUserLinks) { ajax(options); } }); userLinksList.addEventListener("mouseleave", function (evt) { if (!window.__RAPMD__) { return; } window.__RAPMD__.lastUserLinks = null; }); } } // New function to handle dynamic submenu positioning function handleSubMenuPosition() { // Find all parent items that contain a right-facing submenu const parentMenuItems = document.querySelectorAll('.header-dropdown-item:has(.sub-menu-list-right)'); parentMenuItems.forEach(parentItem => { const subMenu = parentItem.querySelector('.sub-menu-list-right'); // Listen for hover to check space and adjust direction parentItem.addEventListener('mouseover', () => { // Temporarily show the submenu to get its width (since it's hidden by default) subMenu.style.display = 'block'; // Calculate critical positions and dimensions const parentRect = parentItem.getBoundingClientRect(); const subMenuWidth = subMenu.offsetWidth; const availableRightSpace = window.innerWidth - parentRect.right; // Toggle the left-expanding class based on available space if (availableRightSpace < subMenuWidth) { subMenu.classList.add('sub-menu-left'); } else { subMenu.classList.remove('sub-menu-left'); } // If your CSS uses a custom `shown()` mixin, replace the temporary display with that // Example: subMenu.classList.add('shown'); }); // Reset the class when hovering out to avoid state carryover parentItem.addEventListener('mouseleave', () => { subMenu.classList.remove('sub-menu-left'); }); }); } // Your existing createMenuList and ajax functions remain unchanged... function createMenuList(items, menuItemEl, emptyListTitle) { if (!menuItemEl) { return; } var df = document.createDocumentFragment(); (items.length ? items : [{ Title: emptyListTitle }]).forEach(function (item) { var li = document.createElement("li"), a = document.createElement("a"); if (item.Url) { a.href = item.Url; } else { a.classList.add("empty-link-item"); } a.innerHTML = item.Title; li.appendChild(a); df.appendChild(li); }); menuItemEl.textContent = ""; menuItemEl.appendChild(df); } function ajax(options) { var url = options.url, method = options.method, successCallback = options.successCallback, failureCallback = options.failureCallback, xhr = new XMLHttpRequest(); xhr.open(method, url); xhr.onload = function () { if (xhr.status === 200 && successCallback) { var response = JSON.parse(xhr.responseText); successCallback(response); } else if (failureCallback) { failureCallback(); } }; xhr.send(); } })();
Key Details to Note:
- Temporary Display: We briefly set
display: blockon the submenu to get its width because hidden elements (display: none) return0foroffsetWidth. If your CSS usesvisibility: hiddenoropacity: 0instead ofdisplay: none, you can skip this step. - Parent Positioning: Ensure your
.header-dropdown-itemhasposition: relative(which it looks like it does from your existing CSS) so the submenu'sright: 100%is relative to the parent's edge. - State Reset: We remove the
.sub-menu-leftclass onmouseleaveto make sure the submenu behaves correctly if the user hovers again later.
Testing
Try hovering over menu items near the right edge of the window—your submenu should now flip to the left if there's not enough space. For items with plenty of room on the right, it'll keep expanding normally.
内容的提问来源于stack exchange,提问作者GGotchaA

