Mobile Menu Optimization ✓ Fixed duplicate script loading issue Improved menu closing behavior after item selection Added smooth scroll for anchor links Enhanced touch targets for better mobile usability Added proper ARIA attributes for accessibility Implemented fade animation for menu transitions Responsive Design Improvements ✓ Adjusted menu height to use viewport units Added proper padding for notched phones Improved touch target sizes Enhanced menu item spacing and interaction feedback Fixed menu scrolling on long content Performance & Accessibility Enhancements ✓ Added meta description for SEO Implemented proper aria-expanded states Added smooth animations with performance considerations Improved event listener efficiency Added empty favicon to prevent 404 errors Code Quality Improvements ✓ Removed duplicate code Improved event handling Enhanced error handling Better organization of CSS media queries Added proper touch device detection All changes maintain existing functionality while improving user experience, accessibility, and performance. The mobile menu now behaves more naturally, with proper closing behavior and smooth transitions.
137 lines
5.4 KiB
JavaScript
137 lines
5.4 KiB
JavaScript
document.addEventListener('DOMContentLoaded', function() {
|
|
// Helper function to handle component loading
|
|
async function loadComponent(url, insertPosition) {
|
|
try {
|
|
const response = await fetch(url);
|
|
if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
|
|
const data = await response.text();
|
|
document.body.insertAdjacentHTML(insertPosition, data);
|
|
|
|
// After header is loaded, initialize its event listeners
|
|
if (url.includes('header.html')) {
|
|
initializeHeader();
|
|
}
|
|
return true;
|
|
} catch (error) {
|
|
console.warn(`Failed to load component from ${url}:`, error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Load components with error handling
|
|
Promise.all([
|
|
loadComponent('components/header.html', 'afterbegin'),
|
|
loadComponent('components/footer.html', 'beforeend')
|
|
]).then(results => {
|
|
if (results.every(Boolean)) {
|
|
// Dispatch custom event when all components are loaded
|
|
document.dispatchEvent(new Event('componentsLoaded'));
|
|
}
|
|
}).catch(error => {
|
|
console.warn('Error loading components:', error);
|
|
});
|
|
|
|
// Initialize header functionality after it's loaded
|
|
function initializeHeader() {
|
|
const header = document.querySelector('header');
|
|
if (!header) return;
|
|
|
|
const pagesToHideLinks = [
|
|
'datenschutz.html',
|
|
'impressum.html'
|
|
];
|
|
|
|
if (pagesToHideLinks.some(page => window.location.pathname.includes(page))) {
|
|
const expertiseLink = document.getElementById('expertise-link');
|
|
const contactLink = document.getElementById('contact-link');
|
|
|
|
if (expertiseLink) expertiseLink.style.display = 'none';
|
|
if (contactLink) contactLink.style.display = 'none';
|
|
}
|
|
|
|
const mobileMenuToggle = header.querySelector('.mobile-menu-toggle');
|
|
const navMenu = header.querySelector('.nav-menu');
|
|
|
|
function closeMenu() {
|
|
navMenu.classList.remove('active');
|
|
const menuIcon = mobileMenuToggle.querySelector('i');
|
|
if (menuIcon) {
|
|
menuIcon.className = 'fas fa-bars';
|
|
}
|
|
mobileMenuToggle.setAttribute('aria-expanded', 'false');
|
|
}
|
|
|
|
// Mobile menu toggle
|
|
if (mobileMenuToggle && navMenu) {
|
|
mobileMenuToggle.setAttribute('aria-expanded', 'false');
|
|
|
|
mobileMenuToggle.addEventListener('click', () => {
|
|
const isExpanded = navMenu.classList.contains('active');
|
|
navMenu.classList.toggle('active');
|
|
const menuIcon = mobileMenuToggle.querySelector('i');
|
|
if (menuIcon) {
|
|
menuIcon.className = isExpanded ? 'fas fa-bars' : 'fas fa-times';
|
|
}
|
|
mobileMenuToggle.setAttribute('aria-expanded', (!isExpanded).toString());
|
|
});
|
|
|
|
// Add click handlers for nav items
|
|
navMenu.querySelectorAll('a').forEach(link => {
|
|
link.addEventListener('click', (e) => {
|
|
// Only close menu if it's a same-page anchor link
|
|
if (link.getAttribute('href').startsWith('#')) {
|
|
e.preventDefault();
|
|
const targetId = link.getAttribute('href').slice(1);
|
|
const targetElement = document.getElementById(targetId);
|
|
if (targetElement) {
|
|
closeMenu();
|
|
targetElement.scrollIntoView({ behavior: 'smooth' });
|
|
}
|
|
}
|
|
closeMenu();
|
|
});
|
|
});
|
|
|
|
// Close menu when clicking outside
|
|
document.addEventListener('click', (e) => {
|
|
if (navMenu.classList.contains('active') &&
|
|
!e.target.closest('.nav-menu') &&
|
|
!e.target.closest('.mobile-menu-toggle')) {
|
|
closeMenu();
|
|
}
|
|
});
|
|
}
|
|
|
|
// Scroll behavior - only enable on index page
|
|
if (window.location.pathname === '/' || window.location.pathname === '/index.html') {
|
|
let lastScrollTop = 0;
|
|
let scrollTimeout;
|
|
|
|
window.addEventListener('scroll', () => {
|
|
if (scrollTimeout) {
|
|
window.cancelAnimationFrame(scrollTimeout);
|
|
}
|
|
|
|
scrollTimeout = window.requestAnimationFrame(() => {
|
|
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
|
|
|
// Don't hide header when near top of page
|
|
if (scrollTop < 100) {
|
|
header.style.transform = 'translateY(0)';
|
|
return;
|
|
}
|
|
|
|
// Hide header on scroll down, show on scroll up
|
|
if (scrollTop > lastScrollTop) {
|
|
header.style.transform = 'translateY(-100%)';
|
|
} else {
|
|
header.style.transform = 'translateY(0)';
|
|
}
|
|
|
|
lastScrollTop = scrollTop;
|
|
});
|
|
}, { passive: true });
|
|
}
|
|
}
|
|
});
|