329 lines
11 KiB
JavaScript
329 lines
11 KiB
JavaScript
document.addEventListener('DOMContentLoaded', () => {
|
|
initThemeToggle();
|
|
initSmoothScroll();
|
|
initFormValidation();
|
|
initAnimations();
|
|
initScrollProgress();
|
|
initStarryBackground();
|
|
initCookieConsent();
|
|
});
|
|
|
|
function initThemeToggle() {
|
|
const themeToggle = document.getElementById('theme-toggle');
|
|
const body = document.body;
|
|
const icon = themeToggle.querySelector('i');
|
|
|
|
// Check for saved theme preference
|
|
const savedTheme = localStorage.getItem('theme');
|
|
if (savedTheme) {
|
|
body.className = savedTheme;
|
|
updateIcon(savedTheme === 'light-theme');
|
|
}
|
|
|
|
// Theme toggle functionality
|
|
themeToggle.addEventListener('click', () => {
|
|
const isLight = body.classList.toggle('light-theme');
|
|
localStorage.setItem('theme', isLight ? 'light-theme' : 'dark-theme');
|
|
updateIcon(isLight);
|
|
});
|
|
|
|
function updateIcon(isLight) {
|
|
icon.className = isLight ? 'fas fa-moon' : 'fas fa-sun';
|
|
icon.style.animation = 'iconBounce 0.5s ease';
|
|
setTimeout(() => icon.style.animation = '', 500);
|
|
}
|
|
}
|
|
|
|
function initScrollProgress() {
|
|
const progressBar = document.querySelector('.scroll-progress');
|
|
|
|
window.addEventListener('scroll', () => {
|
|
const winScroll = document.body.scrollTop || document.documentElement.scrollTop;
|
|
const height = document.documentElement.scrollHeight - document.documentElement.clientHeight;
|
|
const scrolled = (winScroll / height) * 100;
|
|
progressBar.style.transform = `scaleX(${scrolled / 100})`;
|
|
});
|
|
}
|
|
|
|
function initStarryBackground() {
|
|
const starsContainer = document.querySelector('.stars');
|
|
const numStars = 50;
|
|
|
|
// Create stars
|
|
for (let i = 0; i < numStars; i++) {
|
|
const star = document.createElement('div');
|
|
star.className = 'star';
|
|
star.style.width = Math.random() * 3 + 'px';
|
|
star.style.height = star.style.width;
|
|
star.style.left = Math.random() * 100 + '%';
|
|
star.style.top = Math.random() * 100 + '%';
|
|
star.style.animationDelay = Math.random() * 2 + 's';
|
|
starsContainer.appendChild(star);
|
|
}
|
|
}
|
|
|
|
function initSmoothScroll() {
|
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
|
anchor.addEventListener('click', function (e) {
|
|
e.preventDefault();
|
|
const target = document.querySelector(this.getAttribute('href'));
|
|
if (target) {
|
|
const headerOffset = 60;
|
|
const elementPosition = target.getBoundingClientRect().top;
|
|
const offsetPosition = elementPosition + window.pageYOffset - headerOffset;
|
|
|
|
window.scrollTo({
|
|
top: offsetPosition,
|
|
behavior: 'smooth'
|
|
});
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function initFormValidation() {
|
|
const form = document.querySelector('.contact-form');
|
|
if (!form) return;
|
|
|
|
const inputs = {
|
|
name: form.querySelector('#name'),
|
|
email: form.querySelector('#email'),
|
|
phone: form.querySelector('#phone'),
|
|
message: form.querySelector('#message')
|
|
};
|
|
const submitButton = form.querySelector('.submit-button');
|
|
const buttonText = submitButton.querySelector('.button-text');
|
|
const loadingSpinner = submitButton.querySelector('.loading-spinner');
|
|
const feedbackDiv = form.querySelector('.form-feedback');
|
|
const messageCounter = form.querySelector('.char-counter');
|
|
|
|
// Initialize character counter
|
|
if (inputs.message && messageCounter) {
|
|
const maxLength = inputs.message.getAttribute('maxlength');
|
|
updateCharCounter(inputs.message.value.length, maxLength);
|
|
|
|
inputs.message.addEventListener('input', (e) => {
|
|
const length = e.target.value.length;
|
|
updateCharCounter(length, maxLength);
|
|
});
|
|
}
|
|
|
|
function updateCharCounter(current, max) {
|
|
messageCounter.textContent = `${current} / ${max}`;
|
|
// Add visual feedback when approaching limit
|
|
if (current >= max * 0.9) {
|
|
messageCounter.style.color = 'var(--error-red)';
|
|
} else if (current >= max * 0.8) {
|
|
messageCounter.style.color = 'var(--warning-yellow)';
|
|
} else {
|
|
messageCounter.style.color = 'var(--text-muted)';
|
|
}
|
|
}
|
|
|
|
// Real-time validation
|
|
Object.values(inputs).forEach(input => {
|
|
if (!input) return;
|
|
|
|
['input', 'blur'].forEach(eventType => {
|
|
input.addEventListener(eventType, () => {
|
|
validateInput(input);
|
|
updateSubmitButton();
|
|
});
|
|
});
|
|
|
|
// Add focus effects
|
|
input.addEventListener('focus', () => {
|
|
input.closest('.input-wrapper').classList.add('focused');
|
|
});
|
|
|
|
input.addEventListener('blur', () => {
|
|
input.closest('.input-wrapper').classList.remove('focused');
|
|
});
|
|
});
|
|
|
|
function validateInput(input) {
|
|
if (!input.required && input.value === '') {
|
|
// Optional fields are valid when empty
|
|
const wrapper = input.closest('.input-wrapper');
|
|
wrapper.classList.remove('invalid', 'valid');
|
|
return true;
|
|
}
|
|
|
|
const isValid = input.checkValidity();
|
|
const wrapper = input.closest('.input-wrapper');
|
|
|
|
wrapper.classList.toggle('invalid', !isValid);
|
|
wrapper.classList.toggle('valid', isValid);
|
|
|
|
// Show validation message
|
|
let errorMessage = '';
|
|
if (!isValid) {
|
|
if (input.validity.valueMissing) {
|
|
errorMessage = 'Dieses Feld ist erforderlich';
|
|
} else if (input.validity.typeMismatch) {
|
|
errorMessage = 'Bitte geben Sie ein gültiges Format ein';
|
|
} else if (input.validity.patternMismatch) {
|
|
errorMessage = input.title;
|
|
} else if (input.validity.tooShort) {
|
|
const minLength = input.getAttribute('minlength');
|
|
errorMessage = `Mindestens ${minLength} Zeichen erforderlich`;
|
|
}
|
|
}
|
|
|
|
let errorElement = wrapper.querySelector('.error-message');
|
|
if (!errorElement && errorMessage) {
|
|
errorElement = document.createElement('div');
|
|
errorElement.className = 'error-message';
|
|
wrapper.appendChild(errorElement);
|
|
}
|
|
|
|
if (errorElement) {
|
|
if (errorMessage) {
|
|
errorElement.textContent = errorMessage;
|
|
errorElement.style.display = 'flex';
|
|
} else {
|
|
errorElement.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
return isValid;
|
|
}
|
|
|
|
function updateSubmitButton() {
|
|
const requiredInputs = Object.values(inputs).filter(input => input && input.required);
|
|
const isFormValid = requiredInputs.every(input => input.checkValidity());
|
|
submitButton.disabled = !isFormValid;
|
|
|
|
// Visual feedback
|
|
submitButton.classList.toggle('ready', isFormValid);
|
|
}
|
|
|
|
form.addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
|
|
const requiredInputs = Object.values(inputs).filter(input => input && input.required);
|
|
if (!requiredInputs.every(input => validateInput(input))) {
|
|
showFeedback('Bitte füllen Sie alle Pflichtfelder korrekt aus.', false);
|
|
return;
|
|
}
|
|
|
|
// Show loading state
|
|
submitButton.disabled = true;
|
|
buttonText.style.opacity = '0';
|
|
loadingSpinner.style.display = 'block';
|
|
|
|
try {
|
|
// Simulate API call
|
|
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
|
|
showFeedback('Vielen Dank für Ihre Nachricht! Wir werden uns in Kürze bei Ihnen melden.', true);
|
|
form.reset();
|
|
|
|
// Reset validation states
|
|
Object.values(inputs).forEach(input => {
|
|
if (!input) return;
|
|
const wrapper = input.closest('.input-wrapper');
|
|
wrapper.classList.remove('valid', 'invalid', 'focused');
|
|
const errorElement = wrapper.querySelector('.error-message');
|
|
if (errorElement) {
|
|
errorElement.style.display = 'none';
|
|
}
|
|
});
|
|
|
|
// Reset character counter
|
|
if (messageCounter) {
|
|
updateCharCounter(0, inputs.message.getAttribute('maxlength'));
|
|
}
|
|
} catch (error) {
|
|
showFeedback('Es ist ein Fehler aufgetreten. Bitte versuchen Sie es später erneut.', false);
|
|
} finally {
|
|
// Reset button state
|
|
submitButton.disabled = false;
|
|
buttonText.style.opacity = '1';
|
|
loadingSpinner.style.display = 'none';
|
|
submitButton.classList.remove('ready');
|
|
}
|
|
});
|
|
|
|
function showFeedback(message, isSuccess) {
|
|
feedbackDiv.textContent = message;
|
|
feedbackDiv.className = `form-feedback ${isSuccess ? 'success' : 'error'}`;
|
|
feedbackDiv.style.display = 'block';
|
|
|
|
// Scroll feedback into view
|
|
feedbackDiv.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
|
|
|
// Animate feedback
|
|
feedbackDiv.style.animation = 'scaleIn 0.3s ease forwards';
|
|
|
|
// Auto-hide feedback after 5 seconds
|
|
setTimeout(() => {
|
|
feedbackDiv.style.animation = 'fadeOut 0.3s ease forwards';
|
|
setTimeout(() => {
|
|
feedbackDiv.style.display = 'none';
|
|
}, 300);
|
|
}, 5000);
|
|
}
|
|
}
|
|
|
|
function initAnimations() {
|
|
const observerOptions = {
|
|
threshold: 0.1,
|
|
rootMargin: '0px 0px -50px 0px'
|
|
};
|
|
|
|
const observer = new IntersectionObserver((entries) => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
entry.target.classList.add('visible');
|
|
|
|
// Add stagger effect to child elements
|
|
const children = entry.target.querySelectorAll('.animate-stagger');
|
|
children.forEach((child, index) => {
|
|
child.style.animationDelay = `${index * 0.1}s`;
|
|
child.classList.add('visible');
|
|
});
|
|
|
|
observer.unobserve(entry.target);
|
|
}
|
|
});
|
|
}, observerOptions);
|
|
|
|
// Observe elements
|
|
document.querySelectorAll('.service-card, .vision-item, .fade-in-scroll').forEach(el => {
|
|
observer.observe(el);
|
|
});
|
|
}
|
|
|
|
function initCookieConsent() {
|
|
const cookieConsent = document.getElementById('cookie-consent');
|
|
const acceptButton = document.getElementById('accept-cookies');
|
|
const rejectButton = document.getElementById('reject-cookies');
|
|
|
|
// Check if user has already made a choice
|
|
const cookieChoice = localStorage.getItem('cookieChoice');
|
|
if (!cookieChoice) {
|
|
setTimeout(() => {
|
|
cookieConsent.style.display = 'flex';
|
|
cookieConsent.style.animation = 'slideUp 0.5s ease forwards';
|
|
}, 1000);
|
|
}
|
|
|
|
acceptButton.addEventListener('click', () => {
|
|
localStorage.setItem('cookieChoice', 'accepted');
|
|
hideCookieConsent();
|
|
});
|
|
|
|
rejectButton.addEventListener('click', () => {
|
|
localStorage.setItem('cookieChoice', 'rejected');
|
|
hideCookieConsent();
|
|
});
|
|
|
|
function hideCookieConsent() {
|
|
cookieConsent.style.animation = 'slideDown 0.5s ease forwards';
|
|
setTimeout(() => {
|
|
cookieConsent.style.display = 'none';
|
|
}, 500);
|
|
}
|
|
}
|