- Fix impressum.html with correct HRB 110105 and Amtsgericht Düsseldorf - Add English versions of legal pages (impressum-en.html, datenschutz-en.html) - Correct company representatives to Hendrik Gebhardt & Monami Homma - Remove incorrect Marlon Paulse references and Wiesenstraße address - Remove trust-indicators section from website (HTML, CSS, JS) - Add mobile.css and related mobile navigation files 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
196 Zeilen
6.2 KiB
JavaScript
196 Zeilen
6.2 KiB
JavaScript
/**
|
|
* Mobile Navigation Handler
|
|
* Clean, accessible mobile navigation implementation
|
|
*/
|
|
|
|
class MobileNavigation {
|
|
constructor() {
|
|
this.menuToggle = document.querySelector('.mobile-menu-toggle');
|
|
this.mobileMenu = document.querySelector('.nav-menu-mobile');
|
|
this.overlay = document.querySelector('.mobile-menu-overlay');
|
|
this.menuLinks = document.querySelectorAll('.nav-menu-mobile a');
|
|
this.closeButton = document.querySelector('.mobile-menu-close');
|
|
this.isOpen = false;
|
|
|
|
this.init();
|
|
}
|
|
|
|
init() {
|
|
if (!this.menuToggle || !this.mobileMenu) return;
|
|
|
|
// Toggle button click
|
|
this.menuToggle.addEventListener('click', () => this.toggleMenu());
|
|
|
|
// Close button click
|
|
if (this.closeButton) {
|
|
this.closeButton.addEventListener('click', () => this.closeMenu());
|
|
}
|
|
|
|
// Overlay click closes menu
|
|
this.overlay.addEventListener('click', () => this.closeMenu());
|
|
|
|
// Menu links click closes menu
|
|
this.menuLinks.forEach(link => {
|
|
link.addEventListener('click', () => this.closeMenu());
|
|
});
|
|
|
|
// ESC key closes menu
|
|
document.addEventListener('keydown', (e) => {
|
|
if (e.key === 'Escape' && this.isOpen) {
|
|
this.closeMenu();
|
|
}
|
|
});
|
|
|
|
// Prevent body scroll when menu is open
|
|
this.handleBodyScroll();
|
|
}
|
|
|
|
toggleMenu() {
|
|
this.isOpen ? this.closeMenu() : this.openMenu();
|
|
}
|
|
|
|
openMenu() {
|
|
this.isOpen = true;
|
|
this.menuToggle.classList.add('active');
|
|
this.mobileMenu.classList.add('active');
|
|
this.overlay.classList.add('active');
|
|
|
|
// Update ARIA attributes
|
|
this.menuToggle.setAttribute('aria-expanded', 'true');
|
|
this.mobileMenu.setAttribute('aria-hidden', 'false');
|
|
|
|
// Prevent body scroll
|
|
document.body.style.overflow = 'hidden';
|
|
|
|
// Focus management
|
|
setTimeout(() => {
|
|
const firstLink = this.mobileMenu.querySelector('a');
|
|
if (firstLink) firstLink.focus();
|
|
}, 300);
|
|
}
|
|
|
|
closeMenu() {
|
|
this.isOpen = false;
|
|
this.menuToggle.classList.remove('active');
|
|
this.mobileMenu.classList.remove('active');
|
|
this.overlay.classList.remove('active');
|
|
|
|
// Update ARIA attributes
|
|
this.menuToggle.setAttribute('aria-expanded', 'false');
|
|
this.mobileMenu.setAttribute('aria-hidden', 'true');
|
|
|
|
// Restore body scroll
|
|
document.body.style.overflow = '';
|
|
|
|
// Return focus to toggle button
|
|
this.menuToggle.focus();
|
|
}
|
|
|
|
handleBodyScroll() {
|
|
// Save scroll position when menu opens
|
|
let scrollPosition = 0;
|
|
|
|
const observer = new MutationObserver(() => {
|
|
if (this.isOpen) {
|
|
scrollPosition = window.pageYOffset;
|
|
document.body.style.position = 'fixed';
|
|
document.body.style.top = `-${scrollPosition}px`;
|
|
document.body.style.width = '100%';
|
|
} else {
|
|
document.body.style.position = '';
|
|
document.body.style.top = '';
|
|
document.body.style.width = '';
|
|
window.scrollTo(0, scrollPosition);
|
|
}
|
|
});
|
|
|
|
observer.observe(this.mobileMenu, {
|
|
attributes: true,
|
|
attributeFilter: ['class']
|
|
});
|
|
}
|
|
}
|
|
|
|
// Smooth scroll for anchor links
|
|
class SmoothScroll {
|
|
constructor() {
|
|
this.init();
|
|
}
|
|
|
|
init() {
|
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
|
anchor.addEventListener('click', (e) => {
|
|
const href = anchor.getAttribute('href');
|
|
if (href === '#') return;
|
|
|
|
e.preventDefault();
|
|
const target = document.querySelector(href);
|
|
|
|
if (target) {
|
|
const offset = 80; // Account for fixed navbar
|
|
const targetPosition = target.offsetTop - offset;
|
|
|
|
window.scrollTo({
|
|
top: targetPosition,
|
|
behavior: 'smooth'
|
|
});
|
|
}
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
// Responsive image loading
|
|
class ResponsiveImages {
|
|
constructor() {
|
|
this.init();
|
|
}
|
|
|
|
init() {
|
|
// Check if user prefers reduced motion
|
|
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
|
|
if (prefersReducedMotion) {
|
|
// Disable animations
|
|
document.documentElement.style.setProperty('--animation-duration', '0.01s');
|
|
}
|
|
|
|
// Lazy load images on mobile
|
|
if ('IntersectionObserver' in window && window.innerWidth <= 768) {
|
|
const images = document.querySelectorAll('img[data-src]');
|
|
|
|
const imageObserver = new IntersectionObserver((entries) => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
const img = entry.target;
|
|
img.src = img.dataset.src;
|
|
img.removeAttribute('data-src');
|
|
imageObserver.unobserve(img);
|
|
}
|
|
});
|
|
});
|
|
|
|
images.forEach(img => imageObserver.observe(img));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Initialize on DOM load
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
new MobileNavigation();
|
|
new SmoothScroll();
|
|
new ResponsiveImages();
|
|
|
|
// Hide mobile menu button styles until JS loads
|
|
document.documentElement.classList.add('js-loaded');
|
|
});
|
|
|
|
// Handle orientation change
|
|
window.addEventListener('orientationchange', () => {
|
|
// Close mobile menu on orientation change
|
|
const mobileNav = document.querySelector('.nav-menu-mobile');
|
|
if (mobileNav && mobileNav.classList.contains('active')) {
|
|
const event = new Event('click');
|
|
document.querySelector('.mobile-menu-overlay').dispatchEvent(event);
|
|
}
|
|
}); |