Files
fidem/assets/js/main.js

549 lines
19 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Main JavaScript file for FIDEM Medical Center website
document.addEventListener('DOMContentLoaded', function() {
// Mobile menu functionality
initMobileMenu();
// Hero slider functionality
initHeroSlider();
// Smooth scrolling for navigation links
initSmoothScrolling();
// Initialize map
initMap();
// Navbar scroll effect
initNavbarScroll();
// Animation on scroll - плавные анимации появления
initScrollAnimations();
// Service modal functionality
initServiceModal();
});
// Mobile Menu
function initMobileMenu() {
const mobileMenuToggle = document.getElementById('mobile-menu-toggle');
const navbarMenu = document.getElementById('navbar-menu');
const navLinks = document.querySelectorAll('.nav-link');
if (mobileMenuToggle && navbarMenu) {
mobileMenuToggle.addEventListener('click', function() {
navbarMenu.classList.toggle('active');
// Animate hamburger menu
const spans = mobileMenuToggle.querySelectorAll('span');
if (navbarMenu.classList.contains('active')) {
spans[0].style.transform = 'rotate(45deg) translateY(7px)';
spans[1].style.opacity = '0';
spans[2].style.transform = 'rotate(-45deg) translateY(-7px)';
} else {
spans[0].style.transform = 'none';
spans[1].style.opacity = '1';
spans[2].style.transform = 'none';
}
});
// Close mobile menu when clicking on nav links
navLinks.forEach(link => {
link.addEventListener('click', () => {
navbarMenu.classList.remove('active');
const spans = mobileMenuToggle.querySelectorAll('span');
spans[0].style.transform = 'none';
spans[1].style.opacity = '1';
spans[2].style.transform = 'none';
});
});
// Close mobile menu when clicking outside
document.addEventListener('click', function(e) {
if (!navbarMenu.contains(e.target) && !mobileMenuToggle.contains(e.target)) {
navbarMenu.classList.remove('active');
const spans = mobileMenuToggle.querySelectorAll('span');
spans[0].style.transform = 'none';
spans[1].style.opacity = '1';
spans[2].style.transform = 'none';
}
});
}
}
// Hero Slider
function initHeroSlider() {
const slides = document.querySelectorAll('.hero-slide');
const navBtns = document.querySelectorAll('.hero-nav-btn');
const prevBtn = document.querySelector('.hero-arrow-prev');
const nextBtn = document.querySelector('.hero-arrow-next');
let currentSlide = 0;
let slideInterval;
if (slides.length === 0) return;
// Function to show specific slide
function showSlide(index) {
// Remove active class from all slides and nav buttons
slides.forEach(slide => slide.classList.remove('active'));
navBtns.forEach(btn => btn.classList.remove('active'));
// Add active class to current slide and nav button
slides[index].classList.add('active');
if (navBtns[index]) {
navBtns[index].classList.add('active');
}
currentSlide = index;
}
// Function to go to next slide
function nextSlide() {
const next = (currentSlide + 1) % slides.length;
showSlide(next);
}
// Function to go to previous slide
function prevSlide() {
const prev = (currentSlide - 1 + slides.length) % slides.length;
showSlide(prev);
}
// Auto-play functionality
function startAutoPlay() {
slideInterval = setInterval(nextSlide, 5000); // Change slide every 5 seconds
}
function stopAutoPlay() {
clearInterval(slideInterval);
}
// Event listeners for navigation buttons
navBtns.forEach((btn, index) => {
btn.addEventListener('click', () => {
showSlide(index);
stopAutoPlay();
startAutoPlay(); // Restart auto-play
});
});
// Event listeners for arrow buttons
if (nextBtn) {
nextBtn.addEventListener('click', () => {
nextSlide();
stopAutoPlay();
startAutoPlay();
});
}
if (prevBtn) {
prevBtn.addEventListener('click', () => {
prevSlide();
stopAutoPlay();
startAutoPlay();
});
}
// Pause auto-play on hover
const heroSection = document.querySelector('.hero');
if (heroSection) {
heroSection.addEventListener('mouseenter', stopAutoPlay);
heroSection.addEventListener('mouseleave', startAutoPlay);
}
// Start auto-play
startAutoPlay();
// Keyboard navigation
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowLeft') {
prevSlide();
stopAutoPlay();
startAutoPlay();
} else if (e.key === 'ArrowRight') {
nextSlide();
stopAutoPlay();
startAutoPlay();
}
});
}
// Smooth Scrolling
function initSmoothScrolling() {
const navLinks = document.querySelectorAll('a[href^="#"]');
navLinks.forEach(link => {
link.addEventListener('click', function(e) {
e.preventDefault();
const targetId = this.getAttribute('href');
const targetSection = document.querySelector(targetId);
if (targetSection) {
const headerHeight = document.querySelector('.header').offsetHeight;
const targetPosition = targetSection.offsetTop - headerHeight - 20;
window.scrollTo({
top: targetPosition,
behavior: 'smooth'
});
}
});
});
}
// Navbar Scroll Effect
function initNavbarScroll() {
const header = document.querySelector('.header');
let lastScrollY = window.scrollY;
window.addEventListener('scroll', () => {
const currentScrollY = window.scrollY;
if (currentScrollY > 100) {
header.style.background = 'rgba(255, 255, 255, 0.95)';
header.style.backdropFilter = 'blur(10px)';
} else {
header.style.background = '#ffffff';
header.style.backdropFilter = 'none';
}
// Hide/show header on scroll
if (currentScrollY > lastScrollY && currentScrollY > 200) {
header.style.transform = 'translateY(-100%)';
} else {
header.style.transform = 'translateY(0)';
}
lastScrollY = currentScrollY;
});
}
// Initialize Yandex Map
function initMap() {
// Check if Yandex Maps API is loaded
if (typeof ymaps === 'undefined') {
console.log('Yandex Maps API not loaded');
// Create placeholder for map
createMapPlaceholder();
return;
}
ymaps.ready(function() {
// Точные координаты для г. Слоним, ул. Чапаева, 3
const coords = [53.092667, 25.321702];
const map = new ymaps.Map('map', {
center: coords,
zoom: 17,
controls: ['zoomControl', 'fullscreenControl']
});
// Add placemark for medical center
const placemark = new ymaps.Placemark(coords, {
balloonContentHeader: 'Медицинский центр ФИДЕМ',
balloonContentBody: 'г. Слоним, ул. Чапаева, 3',
balloonContentFooter: 'Режим работы: Ежедневно 8:00-20:00, без выходных',
hintContent: 'ФИДЕМ - Медицинский центр'
}, {
iconLayout: 'default#image',
iconImageHref: 'data:image/svg+xml;charset=utf-8,<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><circle cx="16" cy="16" r="12" fill="%2381C784"/><path d="M16 8c-2.2 0-4 1.8-4 4 0 3 4 8 4 8s4-5 4-8c0-2.2-1.8-4-4-4zm0 6c-1.1 0-2-0.9-2-2s0.9-2 2-2 2 0.9 2 2-0.9 2-2 2z" fill="white"/></svg>',
iconImageSize: [32, 32],
iconImageOffset: [-16, -32]
});
map.geoObjects.add(placemark);
// Open balloon on load
setTimeout(() => {
placemark.balloon.open();
}, 1000);
});
}
// Create map placeholder if Yandex Maps API is not available
function createMapPlaceholder() {
const mapContainer = document.getElementById('map');
if (mapContainer) {
mapContainer.innerHTML = `
<div style="display: flex; align-items: center; justify-content: center; height: 100%; background-color: #ecf0f1; color: #7f8c8d; flex-direction: column; text-align: center; padding: 2rem;">
<i class="fas fa-map-marker-alt" style="font-size: 3rem; color: #218F36; margin-bottom: 1rem;"></i>
<h3 style="margin-bottom: 0.5rem; color: #2c3e50;">Наш адрес</h3>
<p style="margin-bottom: 1rem;">г. Слоним, ул. Чапаева, 3</p>
<a href="https://yandex.ru/maps/?text=53.091667,25.315" target="_blank" style="color: #218F36; text-decoration: underline;">
Открыть в Яндекс.Картах
</a>
</div>
`;
}
}
// Utility function to throttle scroll events
function throttle(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// Active navigation link highlighting
function initActiveNavigation() {
const navLinks = document.querySelectorAll('.nav-link');
const sections = document.querySelectorAll('section[id]');
const highlightNavigation = throttle(() => {
const scrollY = window.pageYOffset;
const headerHeight = document.querySelector('.header').offsetHeight;
sections.forEach(section => {
const sectionHeight = section.offsetHeight;
const sectionTop = section.offsetTop - headerHeight - 50;
const sectionId = section.getAttribute('id');
if (scrollY > sectionTop && scrollY <= sectionTop + sectionHeight) {
navLinks.forEach(link => {
link.classList.remove('active');
if (link.getAttribute('href') === `#${sectionId}`) {
link.classList.add('active');
}
});
}
});
}, 100);
window.addEventListener('scroll', highlightNavigation);
}
// Initialize active navigation
document.addEventListener('DOMContentLoaded', initActiveNavigation);
// Phone number formatting for better UX
function formatPhoneNumbers() {
const phoneLinks = document.querySelectorAll('a[href^="tel:"]');
phoneLinks.forEach(link => {
link.addEventListener('click', function(e) {
// Analytics tracking could be added here
console.log('Phone number clicked:', this.href);
});
});
}
// Initialize phone number formatting
document.addEventListener('DOMContentLoaded', formatPhoneNumbers);
// Form validation (if contact form is added later)
function initFormValidation() {
const forms = document.querySelectorAll('form');
forms.forEach(form => {
form.addEventListener('submit', function(e) {
e.preventDefault();
// Basic form validation
const inputs = form.querySelectorAll('input[required], textarea[required]');
let isValid = true;
inputs.forEach(input => {
if (!input.value.trim()) {
isValid = false;
input.classList.add('error');
} else {
input.classList.remove('error');
}
});
if (isValid) {
// Form is valid, submit or process
console.log('Form submitted successfully');
// Add success message or redirect
} else {
console.log('Form validation failed');
}
});
});
}
// Loading animation for images
function initImageLoading() {
const images = document.querySelectorAll('img');
images.forEach(img => {
if (!img.complete) {
img.classList.add('loading');
img.addEventListener('load', function() {
this.classList.remove('loading');
this.classList.add('loaded');
});
}
});
}
// Initialize image loading
document.addEventListener('DOMContentLoaded', initImageLoading);
// Accessibility improvements
function initAccessibility() {
// Skip to main content link
const skipLink = document.createElement('a');
skipLink.href = '#main';
skipLink.textContent = 'Перейти к основному содержанию';
skipLink.className = 'skip-link';
skipLink.style.cssText = `
position: absolute;
top: -40px;
left: 6px;
background: #218F36;
color: white;
padding: 8px;
text-decoration: none;
border-radius: 4px;
z-index: 1001;
transition: top 0.3s;
`;
skipLink.addEventListener('focus', function() {
this.style.top = '6px';
});
skipLink.addEventListener('blur', function() {
this.style.top = '-40px';
});
document.body.insertBefore(skipLink, document.body.firstChild);
// Add main id to main element
const main = document.querySelector('main');
if (main && !main.id) {
main.id = 'main';
}
}
// Initialize accessibility features
document.addEventListener('DOMContentLoaded', initAccessibility);
// Service Cards Expand/Collapse
// Service Modal
function initServiceModal() {
const serviceCards = document.querySelectorAll('.service-card');
const modal = document.getElementById('service-modal');
const modalOverlay = modal.querySelector('.modal-overlay');
const modalClose = modal.querySelector('.modal-close');
const modalIcon = modal.querySelector('.modal-icon i');
const modalTitle = modal.querySelector('.modal-title');
const modalDescription = modal.querySelector('.modal-description');
const modalProcedures = document.getElementById('modal-procedures');
let scrollPosition = 0;
// Открытие модального окна
serviceCards.forEach(card => {
card.addEventListener('click', function() {
const serviceKey = card.getAttribute('data-service');
const serviceData = servicesData[serviceKey];
if (serviceData) {
// Сохраняем текущую позицию скролла
scrollPosition = window.pageYOffset || document.documentElement.scrollTop;
// Заполняем данные модального окна
modalIcon.className = serviceData.icon;
modalTitle.textContent = serviceData.title;
modalDescription.textContent = serviceData.description;
// Очищаем и заполняем список процедур
modalProcedures.innerHTML = '';
serviceData.procedures.forEach(procedure => {
const li = document.createElement('li');
li.textContent = procedure;
modalProcedures.appendChild(li);
});
// Блокируем скролл страницы
document.body.style.position = 'fixed';
document.body.style.top = `-${scrollPosition}px`;
document.body.style.width = '100%';
// Показываем модальное окно
modal.classList.add('active');
}
});
});
// Закрытие модального окна
function closeModal() {
// Сначала плавно скрываем модальное окно
modal.classList.remove('active');
// Через короткую задержку восстанавливаем скролл без эффектов
setTimeout(() => {
const currentScrollPosition = scrollPosition;
document.body.style.position = '';
document.body.style.top = '';
document.body.style.width = '';
// Отключаем smooth scroll временно
document.documentElement.style.scrollBehavior = 'auto';
window.scrollTo(0, currentScrollPosition);
// Возвращаем smooth scroll обратно
setTimeout(() => {
document.documentElement.style.scrollBehavior = 'smooth';
}, 50);
}, 100);
}
modalClose.addEventListener('click', closeModal);
modalOverlay.addEventListener('click', closeModal);
// Закрытие по Escape
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && modal.classList.contains('active')) {
closeModal();
}
});
// Предотвращение скролла при касании модального окна на мобильных
modal.addEventListener('touchmove', function(e) {
if (e.target === modal || e.target === modalOverlay) {
e.preventDefault();
}
}, { passive: false });
}
// Плавные scroll animations
function initScrollAnimations() {
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -20px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry, index) => {
if (entry.isIntersecting && !entry.target.classList.contains('fade-in-up')) {
// Добавляем небольшую задержку для эффекта "волны"
setTimeout(() => {
entry.target.classList.add('fade-in-up');
}, index * 50);
// Отключаем наблюдение после анимации
observer.unobserve(entry.target);
}
});
}, observerOptions);
// Наблюдаем за всеми элементами, которые должны анимироваться
const animateElements = document.querySelectorAll('.service-card, .feature, .contact-item, .section-header, .about-text, .stats-item');
// Изначально скрываем элементы для плавного появления
animateElements.forEach((el, index) => {
el.style.opacity = '0';
el.style.transform = 'translateY(20px)';
observer.observe(el);
});
}