-
Notifications
You must be signed in to change notification settings - Fork 0
Home
// Navigation mobile document.addEventListener('DOMContentLoaded', function() { const navToggle = document.querySelector('.nav-toggle'); const navMenu = document.querySelector('.nav-menu'); const navLinks = document.querySelectorAll('.nav-link');
// Toggle menu mobile
navToggle.addEventListener('click', function() {
navMenu.classList.toggle('active');
navToggle.classList.toggle('active');
});
// Fermer le menu mobile quand on clique sur un lien
navLinks.forEach(link => {
link.addEventListener('click', function() {
navMenu.classList.remove('active');
navToggle.classList.remove('active');
});
});
// Fermer le menu mobile quand on clique en dehors
document.addEventListener('click', function(e) {
if (!navToggle.contains(e.target) && !navMenu.contains(e.target)) {
navMenu.classList.remove('active');
navToggle.classList.remove('active');
}
});
});
// Smooth scrolling pour les liens d'ancrage document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function (e) { e.preventDefault(); const target = document.querySelector(this.getAttribute('href')); if (target) { const headerHeight = document.querySelector('.header').offsetHeight; const targetPosition = target.offsetTop - headerHeight - 20;
window.scrollTo({
top: targetPosition,
behavior: 'smooth'
});
}
});
});
// Animation au scroll const observerOptions = { threshold: 0.1, rootMargin: '0px 0px -50px 0px' };
const observer = new IntersectionObserver(function(entries) { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.style.opacity = '1'; entry.target.style.transform = 'translateY(0)'; } }); }, observerOptions);
// Observer les éléments à animer document.querySelectorAll('.program-card, .stat-card, .step, .contact-item').forEach(el => { el.style.opacity = '0'; el.style.transform = 'translateY(30px)'; el.style.transition = 'opacity 0.6s ease, transform 0.6s ease'; observer.observe(el); });
// Header scroll effect window.addEventListener('scroll', function() { const header = document.querySelector('.header'); if (window.scrollY > 100) { header.style.background = 'rgba(255, 255, 255, 0.95)'; header.style.backdropFilter = 'blur(10px)'; } else { header.style.background = '#FFFFFF'; header.style.backdropFilter = 'none'; } });
// Formulaire de contact document.querySelector('.contact-form').addEventListener('submit', function(e) { e.preventDefault();
// Récupérer les données du formulaire
const formData = new FormData(this);
const data = Object.fromEntries(formData);
// Simulation d'envoi (remplacer par vraie logique d'envoi)
showNotification('Votre demande a été envoyée avec succès ! Nous vous contacterons bientôt.', 'success');
// Reset du formulaire
this.reset();
});
// Fonction pour afficher les notifications
function showNotification(message, type = 'info') {
// Créer l'élément notification
const notification = document.createElement('div');
notification.className = notification notification-${type};
notification.innerHTML = <div class="notification-content"> <span class="notification-message">${message}</span> <button class="notification-close">×</button> </div>;
// Ajouter les styles
notification.style.cssText = `
position: fixed;
top: 100px;
right: 20px;
background: ${type === 'success' ? '#28a745' : type === 'error' ? '#dc3545' : '#007bff'};
color: white;
padding: 15px 20px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 10000;
transform: translateX(100%);
transition: transform 0.3s ease;
max-width: 400px;
`;
// Ajouter au DOM
document.body.appendChild(notification);
// Animer l'entrée
setTimeout(() => {
notification.style.transform = 'translateX(0)';
}, 100);
// Gérer la fermeture
const closeBtn = notification.querySelector('.notification-close');
closeBtn.addEventListener('click', () => {
notification.style.transform = 'translateX(100%)';
setTimeout(() => {
document.body.removeChild(notification);
}, 300);
});
// Auto-fermeture après 5 secondes
setTimeout(() => {
if (document.body.contains(notification)) {
notification.style.transform = 'translateX(100%)';
setTimeout(() => {
if (document.body.contains(notification)) {
document.body.removeChild(notification);
}
}, 300);
}
}, 5000);
}
// Animation des compteurs dans les stats function animateCounters() { const counters = document.querySelectorAll('.stat-number');
counters.forEach(counter => {
const target = parseInt(counter.textContent.replace(/\D/g, ''));
const suffix = counter.textContent.replace(/\d/g, '');
let current = 0;
const increment = target / 50;
const updateCounter = () => {
if (current < target) {
current += increment;
counter.textContent = Math.ceil(current) + suffix;
requestAnimationFrame(updateCounter);
} else {
counter.textContent = target + suffix;
}
};
// Observer pour déclencher l'animation quand visible
const counterObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
updateCounter();
counterObserver.unobserve(entry.target);
}
});
}, { threshold: 0.5 });
counterObserver.observe(counter);
});
}
// Initialiser les animations des compteurs animateCounters();
// Parallax effect pour le hero window.addEventListener('scroll', function() { const scrolled = window.pageYOffset; const hero = document.querySelector('.hero'); const heroContent = document.querySelector('.hero-content');
if (hero && scrolled < hero.offsetHeight) {
heroContent.style.transform = `translateY(${scrolled * 0.5}px)`;
}
});
// Lazy loading pour les images if ('IntersectionObserver' in window) { const imageObserver = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; img.src = img.dataset.src; img.classList.remove('lazy'); imageObserver.unobserve(img); } }); });
document.querySelectorAll('img[data-src]').forEach(img => {
imageObserver.observe(img);
});
}
// Gestion du focus pour l'accessibilité document.addEventListener('keydown', function(e) { if (e.key === 'Tab') { document.body.classList.add('keyboard-navigation'); } });
document.addEventListener('mousedown', function() { document.body.classList.remove('keyboard-navigation'); });
// Validation du formulaire en temps réel const formInputs = document.querySelectorAll('.contact-form input, .contact-form select, .contact-form textarea');
formInputs.forEach(input => { input.addEventListener('blur', function() { validateField(this); });
input.addEventListener('input', function() {
if (this.classList.contains('error')) {
validateField(this);
}
});
});
function validateField(field) { const value = field.value.trim(); let isValid = true; let errorMessage = '';
// Validation selon le type de champ
switch (field.type) {
case 'email':
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (value && !emailRegex.test(value)) {
isValid = false;
errorMessage = 'Veuillez entrer une adresse email valide';
}
break;
case 'tel':
const phoneRegex = /^[\d\s\-\+\(\)]+$/;
if (value && !phoneRegex.test(value)) {
isValid = false;
errorMessage = 'Veuillez entrer un numéro de téléphone valide';
}
break;
default:
if (field.hasAttribute('required') && !value) {
isValid = false;
errorMessage = 'Ce champ est requis';
}
}
// Appliquer les styles de validation
if (isValid) {
field.classList.remove('error');
field.classList.add('valid');
removeErrorMessage(field);
} else {
field.classList.remove('valid');
field.classList.add('error');
showErrorMessage(field, errorMessage);
}
return isValid;
}
function showErrorMessage(field, message) { removeErrorMessage(field);
const errorDiv = document.createElement('div');
errorDiv.className = 'field-error';
errorDiv.textContent = message;
errorDiv.style.cssText = `
color: #dc3545;
font-size: 0.875rem;
margin-top: 5px;
display: block;
`;
field.parentNode.appendChild(errorDiv);
}
function removeErrorMessage(field) { const existingError = field.parentNode.querySelector('.field-error'); if (existingError) { existingError.remove(); } }
// Ajouter les styles CSS pour la validation const validationStyles = document.createElement('style'); validationStyles.textContent = ` .form-group input.error, .form-group select.error, .form-group textarea.error { border-color: #dc3545; box-shadow: 0 0 0 3px rgba(220, 53, 69, 0.1); }
.form-group input.valid,
.form-group select.valid,
.form-group textarea.valid {
border-color: #28a745;
box-shadow: 0 0 0 3px rgba(40, 167, 69, 0.1);
}
.keyboard-navigation *:focus {
outline: 2px solid #F4A261;
outline-offset: 2px;
}
.notification-content {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
}
.notification-close {
background: none;
border: none;
color: white;
font-size: 1.5rem;
cursor: pointer;
padding: 0;
line-height: 1;
}
.notification-close:hover {
opacity: 0.8;
}
`;
document.head.appendChild(validationStyles);
console.log('Site web CPPJP initialisé avec succès!');