Просмотр исходного кода

refactor(js): improve menu handlers

Frédéric Guillot 8 месяцев назад
Родитель
Сommit
07246e2b59
2 измененных файлов с 67 добавлено и 63 удалено
  1. 65 49
      internal/ui/static/js/app.js
  2. 2 14
      internal/ui/static/js/bootstrap.js

+ 65 - 49
internal/ui/static/js/app.js

@@ -270,7 +270,7 @@ function goToListItem(offset) {
         if (items[i].classList.contains("current-item")) {
             items[i].classList.remove("current-item");
 
-            // By default adjust selection by offset
+            // By default adjust selection to the next item
             let itemOffset = (i + offset + items.length) % items.length;
             // Allow jumping to top or bottom
             if (offset === TOP) {
@@ -338,73 +338,85 @@ async function triggerWebShare(title, url) {
     window.location.reload();
 }
 
-// make logo element as button on mobile layout
-function checkMenuToggleModeByLayout() {
+/**
+ * Toggle the ARIA attributes on the main menu based on the viewport width.
+ */
+function toggleAriaAttributesOnMainMenu() {
     const logoElement = document.querySelector(".logo");
-    if (!logoElement) return;
-
     const homePageLinkElement = document.querySelector(".logo > a");
 
-    if (document.documentElement.clientWidth < 620) {
+    if (!logoElement || !homePageLinkElement) return;
+
+    const isMobile = document.documentElement.clientWidth < 650;
+
+    if (isMobile) {
         const navMenuElement = document.getElementById("header-menu");
-        const navMenuElementIsExpanded = navMenuElement.classList.contains("js-menu-show");
-        const logoToggleButtonLabel = logoElement.getAttribute("data-toggle-button-label");
-        logoElement.setAttribute("role", "button");
-        logoElement.setAttribute("tabindex", "0");
-        logoElement.setAttribute("aria-label", logoToggleButtonLabel);
-        logoElement.setAttribute("aria-expanded", navMenuElementIsExpanded?"true":"false");
-        homePageLinkElement.setAttribute("tabindex", "-1");
+        const isExpanded = navMenuElement?.classList.contains("js-menu-show") ?? false;
+        const toggleButtonLabel = logoElement.getAttribute("data-toggle-button-label");
+
+        // Set mobile menu button attributes
+        Object.assign(logoElement, {
+            role: "button",
+            tabIndex: 0,
+            ariaLabel: toggleButtonLabel,
+            ariaExpanded: isExpanded.toString()
+        });
+        homePageLinkElement.tabIndex = -1;
     } else {
-        logoElement.removeAttribute("role");
-        logoElement.removeAttribute("tabindex");
-        logoElement.removeAttribute("aria-expanded");
-        logoElement.removeAttribute("aria-label");
+        // Remove mobile menu button attributes
+        ["role", "tabindex", "aria-expanded", "aria-label"].forEach(attr =>
+            logoElement.removeAttribute(attr)
+        );
         homePageLinkElement.removeAttribute("tabindex");
     }
 }
 
-function fixVoiceOverDetailsSummaryBug() {
-    document.querySelectorAll("details").forEach((details) => {
-        const summaryElement = details.querySelector("summary");
-        summaryElement.setAttribute("role", "button");
-        summaryElement.setAttribute("aria-expanded", details.open? "true": "false");
-
-        details.addEventListener("toggle", () => {
-            summaryElement.setAttribute("aria-expanded", details.open? "true": "false");
-        });
-    });
-}
-
-// Show and hide the main menu on mobile devices.
-function toggleMainMenu(event) {
-    if (event.type === "keydown" && !(event.key === "Enter" || event.key === " ")) {
+/**
+ * Toggle the main menu dropdown.
+ *
+ * @param {Event} event - The event object.
+ */
+function toggleMainMenuDropdown(event) {
+    // Only handle Enter, Space, or click events
+    if (event.type === "keydown" && !["Enter", " "].includes(event.key)) {
         return;
     }
 
+    // Prevent default only if element has role attribute (mobile menu button)
     if (event.currentTarget.getAttribute("role")) {
         event.preventDefault();
     }
 
-    const menu = document.querySelector(".header nav ul");
+    const navigationMenu = document.querySelector(".header nav ul");
     const menuToggleButton = document.querySelector(".logo");
-    if (menu.classList.contains("js-menu-show")) {
-        menuToggleButton.setAttribute("aria-expanded", "false");
-    } else {
-        menuToggleButton.setAttribute("aria-expanded", "true");
+
+    if (!navigationMenu || !menuToggleButton) {
+        return;
     }
-    menu.classList.toggle("js-menu-show");
+
+    const isShowing = navigationMenu.classList.toggle("js-menu-show");
+    menuToggleButton.setAttribute("aria-expanded", isShowing.toString());
 }
 
-// Handle click events for the main menu (<li> and <a>).
-function onClickMainMenuListItem(event) {
-    const element = event.target;
+/**
+ * Initialize the main menu handlers.
+ */
+function initializeMainMenuHandlers() {
+    toggleAriaAttributesOnMainMenu();
+    window.addEventListener("resize", toggleAriaAttributesOnMainMenu, { passive: true });
 
-    if (element.tagName === "A") {
-        window.location.href = element.getAttribute("href");
-    } else {
-        const linkElement = element.querySelector("a") || element.closest("a");
-        window.location.href = linkElement.getAttribute("href");
+    const logoElement = document.querySelector(".logo");
+    if (logoElement) {
+        logoElement.addEventListener("click", toggleMainMenuDropdown);
+        logoElement.addEventListener("keydown", toggleMainMenuDropdown);
     }
+
+    onClick(".header nav li", (event) => {
+        const linkElement = event.target.closest("a") || event.target.querySelector("a");
+        if (linkElement) {
+            window.location.href = linkElement.getAttribute("href");
+        }
+    });
 }
 
 /**
@@ -412,7 +424,7 @@ function onClickMainMenuListItem(event) {
  *
  * @returns {void}
  */
-function disableSubmitButtonsOnFormSubmit() {
+function initializeFormHandlers() {
     document.querySelectorAll("form").forEach((element) => {
         element.onsubmit = () => {
             const buttons = element.querySelectorAll("button[type=submit]");
@@ -426,13 +438,17 @@ function disableSubmitButtonsOnFormSubmit() {
     });
 }
 
-// Show modal dialog with the list of keyboard shortcuts.
+/**
+ * Show the keyboard shortcuts modal.
+ */
 function showKeyboardShortcuts() {
     const template = document.getElementById("keyboard-shortcuts");
     ModalHandler.open(template.content, "dialog-title");
 }
 
-// Mark as read visible items of the current page.
+/**
+ * Mark all visible entries on the current page as read.
+ */
 function markPageAsRead() {
     const items = getVisibleEntries();
     const entryIDs = [];

+ 2 - 14
internal/ui/static/js/bootstrap.js

@@ -1,4 +1,5 @@
-disableSubmitButtonsOnFormSubmit();
+initializeMainMenuHandlers();
+initializeFormHandlers();
 initializeMediaPlayerHandlers();
 
 // Initialize the keyboard shortcuts if enabled.
@@ -85,19 +86,6 @@ onAuxClick("a[data-original-link='true']", (event) => {
     }
 }, true);
 
-checkMenuToggleModeByLayout();
-window.addEventListener("resize", checkMenuToggleModeByLayout, { passive: true });
-
-fixVoiceOverDetailsSummaryBug();
-
-const logoElement = document.querySelector(".logo");
-if (logoElement) {
-    logoElement.addEventListener("click", toggleMainMenu);
-    logoElement.addEventListener("keydown", toggleMainMenu);
-}
-
-onClick(".header nav li", (event) => onClickMainMenuListItem(event));
-
 // Register the service worker if supported.
 if ("serviceWorker" in navigator) {
     const serviceWorkerURL = document.body.dataset.serviceWorkerUrl;