/
Vladislav81
/
websiteForMe
Обзор
Документация
Войти
/
Vladislav81
/
websiteForMe
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
static/script.js
310 строк
15 KB
Владислав Антонов
upload files
28 дек 2025, 00:59
28 дек 2025, 00:59
7fa7e44
Код
Авторство
О чём код?
// Скрипт для подсветки активной ссылки при скролле document.addEventListener('DOMContentLoaded', function() { const navLinks = document.querySelectorAll('.navbar-nav .nav-link'); navLinks.forEach(link => { link.addEventListener('click', function() { // Убираем активный класс у всех ссылок navLinks.forEach(item => item.classList.remove('active')); // Добавляем активный класс к текущей ссылке this.classList.add('active'); // Для мобильных: закрываем меню после клика if (window.innerWidth < 992) { const navbarCollapse = document.querySelector('.navbar-collapse'); if (navbarCollapse.classList.contains('show')) { const bsCollapse = new bootstrap.Collapse(navbarCollapse); bsCollapse.hide(); } } }); }); // Можно добавить отслеживание скролла для автоматической смены активного раздела // (потребуется добавить соответствующие id секциям на странице) }); // Кастомизация работы карусели document.addEventListener('DOMContentLoaded', function() { const aboutCarousel = document.getElementById('aboutCarousel'); // Автоматическое переключение каждые 5 секунд const carousel = new bootstrap.Carousel(aboutCarousel, { interval: 5000, wrap: true, pause: 'hover' }); // Добавляем анимацию появления текста при смене слайда aboutCarousel.addEventListener('slide.bs.carousel', function(event) { const activeItem = event.relatedTarget; const captions = activeItem.querySelector('.carousel-caption'); // Сбрасываем анимацию captions.style.opacity = '0'; captions.style.transform = 'translateY(20px)'; // Запускаем анимацию с небольшой задержкой setTimeout(() => { captions.style.transition = 'opacity 0.5s ease, transform 0.5s ease'; captions.style.opacity = '1'; captions.style.transform = 'translateY(0)'; }, 300); }); // Инициализируем первую анимацию const firstCaption = document.querySelector('.carousel-item.active .carousel-caption'); firstCaption.style.opacity = '1'; firstCaption.style.transform = 'translateY(0)'; }); document.addEventListener('DOMContentLoaded', function() { // Обработка кнопок "Подробнее" const serviceButtons = document.querySelectorAll('.btn-service'); serviceButtons.forEach(button => { button.addEventListener('click', function() { const cardTitle = this.closest('.service-card').querySelector('.card-title').textContent; // В реальном проекте здесь будет открытие модального окна или переход на страницу услуги alert(`Подробная информация по услуге: "${cardTitle}"\n\nВ реальном сайте здесь будет открыто подробное описание услуги с ценами, расписанием и формой записи.`); // Анимация нажатия this.style.transform = 'scale(0.95)'; setTimeout(() => { this.style.transform = ''; }, 200); }); }); // Анимация появления карточек при скролле const observerOptions = { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.style.opacity = '1'; entry.target.style.transform = 'translateY(0)'; } }); }, observerOptions); // Применяем анимацию ко всем карточкам услуг const serviceCards = document.querySelectorAll('.service-card'); serviceCards.forEach(card => { card.style.opacity = '0'; card.style.transform = 'translateY(30px)'; card.style.transition = 'opacity 0.5s ease, transform 0.5s ease'; observer.observe(card); }); }); document.addEventListener('DOMContentLoaded', function() { // Настройка карусели отзывов const reviewsCarousel = document.getElementById('reviewsCarousel'); const carousel = new bootstrap.Carousel(reviewsCarousel, { interval: 6000, wrap: true, pause: 'hover' }); // Анимация появления отзывов при смене слайда reviewsCarousel.addEventListener('slide.bs.carousel', function(event) { const activeSlide = event.relatedTarget; const reviewCards = activeSlide.querySelectorAll('.review-card'); // Сбрасываем анимацию reviewCards.forEach(card => { card.style.opacity = '0'; card.style.transform = 'translateY(30px)'; }); // Запускаем анимацию с задержкой для каждого отзыва reviewCards.forEach((card, index) => { setTimeout(() => { card.style.transition = 'opacity 0.6s ease, transform 0.6s ease'; card.style.opacity = '1'; card.style.transform = 'translateY(0)'; }, index * 200); // Задержка между отзывами }); }); // Инициализация анимации для первого слайда const firstCards = document.querySelector('.carousel-item.active .review-card'); if (firstCards) { document.querySelectorAll('.carousel-item.active .review-card').forEach(card => { card.style.opacity = '1'; card.style.transform = 'translateY(0)'; }); } // Клик по карточке отзыва (для увеличения) const reviewCards = document.querySelectorAll('.review-card'); reviewCards.forEach(card => { card.addEventListener('click', function() { const reviewText = this.querySelector('.review-text'); // Переключение высоты текста (свернуть/развернуть) if (reviewText.style.maxHeight && reviewText.style.maxHeight !== 'none') { reviewText.style.maxHeight = 'none'; this.style.cursor = 'default'; } else { // В реальном проекте здесь можно сделать модальное окно const reviewName = this.querySelector('.review-name').textContent; alert(`Полный отзыв от ${reviewName} будет показан в отдельном окне.`); } }); }); }); document.addEventListener('DOMContentLoaded', function() { // Анимация появления элементов const animatedElements = document.querySelectorAll('.fade-in-up'); const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const delay = entry.target.classList.contains('delay-1') ? 200 : entry.target.classList.contains('delay-2') ? 400 : entry.target.classList.contains('delay-3') ? 600 : entry.target.classList.contains('delay-4') ? 800 : 0; setTimeout(() => { entry.target.style.animationPlayState = 'running'; }, delay); observer.unobserve(entry.target); } }); }, { threshold: 0.1 }); animatedElements.forEach(element => { element.style.animationPlayState = 'paused'; observer.observe(element); }); // Инициализация анимации для элементов в видимой области animatedElements.forEach(element => { const rect = element.getBoundingClientRect(); if (rect.top < window.innerHeight && rect.bottom > 0) { element.style.animationPlayState = 'running'; } }); }); // Функция копирования email function copyEmail() { const email = 'ivan@example.com'; const copyBtn = event.currentTarget; // Копирование в буфер обмена navigator.clipboard.writeText(email).then(() => { // Визуальная обратная связь const originalText = copyBtn.innerHTML; copyBtn.innerHTML = '<i class="bi bi-check me-1"></i>Скопировано!'; copyBtn.classList.add('copied'); // Возврат к исходному виду через 2 секунды setTimeout(() => { copyBtn.innerHTML = originalText; copyBtn.classList.remove('copied'); }, 2000); // Дополнительное уведомление showNotification('Email скопирован в буфер обмена!'); }).catch(err => { console.error('Ошибка копирования: ', err); showNotification('Не удалось скопировать email', true); }); } // Функция показа уведомления function showNotification(message, isError = false) { // Создаем элемент уведомления const notification = document.createElement('div'); notification.className = `position-fixed bottom-0 end-0 m-3 p-3 rounded shadow-lg ${isError ? 'bg-danger' : 'bg-success'}`; notification.style.zIndex = '9999'; notification.style.color = 'white'; notification.style.maxWidth = '300px'; notification.style.transition = 'transform 0.3s ease'; notification.innerHTML = ` <div class="d-flex align-items-center"> <i class="bi ${isError ? 'bi-x-circle' : 'bi-check-circle'} me-2"></i> <div>${message}</div> </div> `; // Добавляем на страницу document.body.appendChild(notification); // Анимация появления setTimeout(() => { notification.style.transform = 'translateY(0)'; }, 10); // Удаляем через 3 секунды setTimeout(() => { notification.style.transform = 'translateY(100px)'; setTimeout(() => { document.body.removeChild(notification); }, 300); }, 3000); } // Открытие почтового клиента при клике на email document.querySelectorAll('a[href^="mailto:"]').forEach(link => { link.addEventListener('click', function(e) { // Можно добавить дополнительную логику console.log('Открытие почтового клиента для:', this.href); }); }); // перехват отправки формы для записи на занятия document.getElementById("bookingForm").addEventListener("submit",async function (e) { e.preventDefault(); const alertBox = document.getElementById("booking-alert"); const formData = new FormData(form); try { const response = await fetch("/booking", { method: "POST", body: formData }); const data = await response.json(); if (data.success) { alertBox.className = "alert alert-success"; alertBox.textContent = data.message; alertBox.classList.remove("d-none"); form.reset(); // очистить форму const modalEl = document.getElementById("bookingModal"); const modal = bootstrap.Modal.getInstance(modalEl); modal.hide(); } else { alertBox.className = "alert alert-danger"; alertBox.textContent = data.message; alertBox.classList.remove("d-none"); } } catch (error) { alertBox.className = "alert alert-danger"; alertBox.textContent = "Ошибка соединения с сервером"; alertBox.classList.remove("d-none"); } })