1/** 2 * Universal Menu Handler 3 * Works with and without native <dialog> support 4 */ 5(function() { 6 'use strict'; 7 8 const menuToggle = document.getElementById('menu-toggle'); 9 const menuClose = document.getElementById('menu-close'); 10 const menuDialog = document.getElementById('page__menu'); 11 12 if (!menuToggle || !menuClose || !menuDialog) { 13 return; 14 } 15 16 // Check if dialog is supported 17 const supportsDialog = typeof menuDialog.showModal === 'function'; 18 19 console.log('LDN Flotilla: Dialog support:', supportsDialog ? 'Yes' : 'No'); 20 21 if (supportsDialog) { 22 // Native dialog support 23 console.log('LDN Flotilla: Using native dialog'); 24 25 // The onclick attributes in HTML handle opening 26 // ESC key and backdrop clicks are handled natively 27 28 // Add focus management 29 menuDialog.addEventListener('close', function() { 30 menuToggle.focus(); 31 }); 32 33 // Focus first menu item when opened 34 const observer = new MutationObserver(function(mutations) { 35 mutations.forEach(function(mutation) { 36 if (mutation.type === 'attributes' && mutation.attributeName === 'open') { 37 if (menuDialog.hasAttribute('open')) { 38 const firstMenuItem = menuDialog.querySelector('.menu__list a'); 39 if (firstMenuItem) { 40 setTimeout(() => firstMenuItem.focus(), 100); 41 } 42 } 43 } 44 }); 45 }); 46 observer.observe(menuDialog, { attributes: true }); 47 48 } else { 49 // Fallback for browsers without dialog support 50 console.log('LDN Flotilla: Using fallback implementation'); 51 52 // Remove onclick from button and form method 53 menuToggle.removeAttribute('onclick'); 54 const form = menuDialog.querySelector('form[method="dialog"]'); 55 if (form) { 56 form.removeAttribute('method'); 57 } 58 59 // Create backdrop 60 const backdrop = document.createElement('div'); 61 backdrop.className = 'menu-fallback-backdrop'; 62 backdrop.style.cssText = ` 63 position: fixed; 64 top: 0; 65 left: 0; 66 width: 100%; 67 height: 100%; 68 background-color: rgba(0, 0, 0, 0.1); 69 z-index: 1000; 70 display: none; 71 opacity: 0; 72 transition: opacity 0.3s ease; 73 `; 74 document.body.appendChild(backdrop); 75 76 // Style menu for fallback 77 menuDialog.style.position = 'fixed'; 78 menuDialog.style.top = '0'; 79 menuDialog.style.right = '1rem'; 80 menuDialog.style.width = 'min(280px, calc(100vw - 2rem))'; 81 menuDialog.style.height = 'fit-content'; 82 menuDialog.style.maxHeight = '100vh'; 83 menuDialog.style.zIndex = '1001'; 84 menuDialog.style.display = 'none'; 85 86 function openMenu() { 87 backdrop.style.display = 'block'; 88 menuDialog.style.display = 'flex'; 89 document.body.style.overflow = 'hidden'; 90 91 // Force reflow 92 backdrop.offsetHeight; 93 94 backdrop.style.opacity = '1'; 95 menuDialog.setAttribute('open', ''); 96 97 // Focus first menu item 98 const firstMenuItem = menuDialog.querySelector('.menu__list a'); 99 if (firstMenuItem) { 100 setTimeout(() => firstMenuItem.focus(), 100); 101 } 102 } 103 104 function closeMenu() { 105 backdrop.style.opacity = '0'; 106 menuDialog.removeAttribute('open'); 107 108 setTimeout(() => { 109 backdrop.style.display = 'none'; 110 menuDialog.style.display = 'none'; 111 document.body.style.overflow = ''; 112 }, 300); 113 114 menuToggle.focus(); 115 } 116 117 // Event listeners 118 menuToggle.addEventListener('click', openMenu); 119 menuClose.addEventListener('click', closeMenu); 120 backdrop.addEventListener('click', closeMenu); 121 122 // ESC key support 123 document.addEventListener('keydown', function(e) { 124 if (e.key === 'Escape' && menuDialog.hasAttribute('open')) { 125 closeMenu(); 126 } 127 }); 128 } 129})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.