// управляющий класс App с методом init(), в котором собраны все используемые методы с комментариями о том, что конкретно делает каждый метод class App { constructor() { this.patternPhone = /^(\+7|7|8)?[\s\-]?\(?[489][0-9]{2}\)?[\s\-]?[0-9]{3}[\s\-]?[0-9]{2}[\s\-]?[0-9]{2}$/; // рег. выражение для поля 'телефон'; this.patternEmail = /^[a-zA-Z0-9._%+-\.]+@[a-z0-9.-]+\.[a-z]{2,}$/i; // рег. выражение для поля 'электронная почта'; } init() { console.log('init'); this.stickyHeader(); // липкий хедер; this.controlBurgerMenu(); // бургер-меню; this.smoothScroll(); // плавный скролл к якорю (smooth scroll); this.scrollUp(); // кнопка наверх; this.addToFavorites(); // добавить в избранное (звёздочка); this.initTypicalSlider(); // типовые слайдеры; this.initPartnerslSlider(); // слайдер с партнёрами; this.controlFilters(); // фильтры на главном экране; this.controlPopups(); // открытие/закрытие поп-апов; this.controlContactUsPopup(); // открытие/закрытие поп-апа 'обратный звонок'; this.sendForm('.js_popup_feedback_form', '[data-popup="success"]'); // отправка формы в поп-апе обратной связи; this.sendForm('.js_popup_viewing_form', '[data-popup="success"]'); // отправка формы в поп-апе 'записаться на просмотр'; this.sendForm('.js_footer_feedback_form', '[data-popup="success"]'); // отправка формы в футере; this.sendForm('.js_contacts_form', '.js_contacts_success'); // отправка формы на странице контакты; this.sendForm('.js_popup_sending_form_', '[data-popup="success"]'); //this.sendOffer(); //отправка предложения по e-mail; this.setGeneralMap(); // карта на странице карт; this.setComplexMap('complex-map', [55.726591050908745, 37.57244549999999], 'ЖК Садовые кварталы'); // карта на странице 'ЖК'; this.setComplexMap('offer-map', [55.70851106903402, 37.65864349999999], 'Аренда торгового помещения 321,6 м2'); // карта на странице 'Предложение'; this.setCatalogSorts(); // сортировка на странице 'каталог'; this.initIntroSlider(); // слайдер на странице жк и на странице предложения; this.setTabs('.js_offer_side_tab', '.js_offer_side_item'); // табы с планами объекат и этажа на странице предложения; this.setTabs('.js_offer_side_popup_tab', '.js_offer_side_popup_item'); // табы с планами объекат и этажа в поп-апе на странице предложения; this.sontrolOfferSidePopup(); // логика открытия нужного таба при открытии поп-апа с планами объекат и этажа на странице предложения; this.setCustomGallery(); // галлерея; this.setCookies() // куки; this.setFooterSpoilers() // аккордеон в футере; } // фиксация fixBodyPosition() { setTimeout(function () { // ставим необходимую задержку, чтобы не было «конфликта» в случае, если функция фиксации вызывается сразу после расфиксации (расфиксация отменяет действия расфиксации из-за одновременного действия) if (!document.body.hasAttribute('data-body-scroll-fix')) { // получаем позицию прокрутки let scrollPosition = window.pageYOffset || document.documentElement.scrollTop; // ставим нужные стили document.body.setAttribute('data-body-scroll-fix', scrollPosition); // Cтавим атрибут со значением прокрутки document.body.style.overflow = 'hidden'; document.body.style.position = 'fixed'; document.body.style.top = '-' + scrollPosition + 'px'; document.body.style.left = '0'; document.body.style.width = '100%'; if (window.innerWidth >= 1200) { document.body.style.paddingRight = '8px'; } } }, 15); // можно задержку ещё меньше, но работает хорошо именно с этим значением на всех устройствах и браузерах } // расфиксация unfixBodyPosition() { if (document.body.hasAttribute('data-body-scroll-fix')) { // получаем позицию прокрутки из атрибута let scrollPosition = document.body.getAttribute('data-body-scroll-fix'); // удаляем атрибут document.body.removeAttribute('data-body-scroll-fix'); // удаляем ненужные стили document.body.style.overflow = ''; document.body.style.position = ''; document.body.style.top = ''; document.body.style.left = ''; document.body.style.width = ''; document.body.style.paddingRight = ''; // прокручиваем страницу на полученное из атрибута значение window.scroll(0, scrollPosition); } } // бургер-меню controlBurgerMenu() { const headerBurger = document.querySelector('.js_header_burger'); if (headerBurger) { const menu = document.querySelector('.js_menu'); const menuClose = menu.querySelector('.js_menu_close'); headerBurger.addEventListener('click', () => { menu.classList.add('active'); this.fixBodyPosition(); }); menu.addEventListener('click', (e) => { if (e.target == menu) { menu.classList.remove('active'); this.unfixBodyPosition(); } }); menuClose.addEventListener('click', () => { menu.classList.remove('active'); this.unfixBodyPosition(); }); } } // липкий хедер stickyHeader() { const header = document.querySelector('.js_header'); if (header) { window.addEventListener('scroll', () => { if (window.scrollY > 200) { header.classList.add('fixed'); } else { header.classList.remove('fixed'); } }); }; } // плавный скролл к якорю (smooth scroll) smoothScroll() { const smoothLinks = document.querySelectorAll('.js_smooth_link'); if (smoothLinks.length) { smoothLinks.forEach(link => { link.addEventListener('click', function (e) { e.preventDefault(); let href = this.getAttribute('href').substring(1); const scrollTarget = document.getElementById(href); // const topOffset = document.querySelector('.header').offsetHeight; const topOffset = 0; // если не нужен отступ сверху const elementPosition = scrollTarget.getBoundingClientRect().top; const offsetPosition = elementPosition - topOffset; window.scrollBy({ top: offsetPosition, behavior: 'smooth' }); }); }); } } // кнопка наверх scrollUp() { const toTopBtn = document.querySelector('.js_btn_up'); if (toTopBtn) { toTopBtn.addEventListener('click', function () { window.scrollTo({ top: 0, behavior: 'smooth' }); }); } } // добавить в избранное (звёздочка) addToFavorites() { const cardFavorites = document.querySelectorAll('.js_card_favorites'); if (cardFavorites.length) { cardFavorites.forEach(item => { item.addEventListener('click', (e) => { e.preventDefault(); item.classList.toggle('active'); }); }); } } // типовые слайдеры initTypicalSlider() { const slidersWraps = document.querySelectorAll('.slider__wrap'); if (slidersWraps.length) { slidersWraps.forEach(wrap => { const slider = wrap.querySelector('.swiper'); const prev = wrap.querySelector('.swiper-button-prev'); const next = wrap.querySelector('.swiper-button-next'); const pagination = wrap.querySelector('.swiper-pagination'); let swiper1 = new Swiper(slider, { navigation: { nextEl: next, prevEl: prev, }, pagination: { el: pagination, clickable: true, }, slidesPerView: 1, spaceBetween: 20, observer: true, observeParents: true, observeSlideChildren: true, breakpoints: { 480: { slidesPerView: 1.5, }, 640: { slidesPerView: 2, }, 780: { slidesPerView: 2.5, }, 920: { slidesPerView: 3, }, 1024: { slidesPerView: 3.4 }, 1200: { slidesPerView: 4, } } }); }); } } // метод, делающий число удобночитаемым (добавляет пробел справа через каждые 3 цифры) prettify(num) { const withoutSpace = num.replace(/[^\d]/g, ''); //убирает все символы; return withoutSpace.replace(/(?!^)(?=(?:\d{3})+(?:\.|$))/gm, ' '); //ставит пробелы; } // фильтры на главном экране controlFilters() { const heroFilters = document.querySelectorAll('.js_hero_filter'); const heroSearchBtns = document.querySelectorAll('.js_hero_search_btn'); if (heroFilters.length) { heroFilters.forEach(filter => { const heroFilterInput = filter.querySelector('.js_hero_filter_input'); const heroFilterCurrent = filter.querySelector('.js_hero_filter_current'); const heroFilterItems = filter.querySelectorAll('.hero-filter__item'); const heroFilterFields = filter.querySelectorAll('.js_hero_filter_field'); const heroFilterFrom = filter.querySelector('.js_hero_filter_from'); const heroFilterTo = filter.querySelector('.js_hero_filter_to'); const heroFilterReset = filter.querySelector('.js_hero_filter_reset'); heroFilterCurrent.addEventListener('click', () => { if (filter.classList.contains('active')) { filter.classList.remove('active'); heroSearchBtns.forEach(btn => { btn.disabled = false; }); } else { heroFilters.forEach(filter => { filter.classList.remove('active'); }); filter.classList.add('active'); heroSearchBtns.forEach(btn => { btn.disabled = true; }); } }); if (heroFilterItems.length) { heroFilterItems.forEach(item => { item.addEventListener('click', () => { heroFilterCurrent.textContent = item.textContent; heroFilterInput.value = item.dataset.val; filter.classList.remove('active'); heroSearchBtns.forEach(btn => { btn.disabled = false; }); }); }); } if (heroFilterFields.length) { const heroFilterMin = heroFilterFrom.dataset.min; const heroFilterMax = heroFilterTo.dataset.max; let heroFilterFromVal; let heroFilterToVal; heroFilterFields.forEach(field => { field.addEventListener('input', () => { field.value = this.prettify(field.value); heroFilterReset.classList.remove('active'); heroFilterFields.forEach(field => { if (field.value != "") { heroFilterReset.classList.add('active'); } }); }); }); heroFilterFrom.addEventListener('change', () => { heroFilterFromVal = +heroFilterFrom.value.replace(/\s/g, ''); heroFilterToVal = +heroFilterTo.value.replace(/\s/g, ''); if (heroFilterToVal != '' && heroFilterFromVal > heroFilterToVal) { heroFilterFrom.value = heroFilterTo.value; } else if (heroFilterFromVal < +heroFilterMin) { heroFilterFrom.value = this.prettify(heroFilterMin); } else if (heroFilterFromVal > +heroFilterMax) { heroFilterFrom.value = this.prettify(heroFilterMax); } }); heroFilterTo.addEventListener('change', () => { heroFilterFromVal = +heroFilterFrom.value.replace(/\s/g, ''); heroFilterToVal = +heroFilterTo.value.replace(/\s/g, ''); if (heroFilterFromVal != '' && heroFilterToVal < heroFilterFromVal) { heroFilterTo.value = heroFilterFrom.value; } else if (heroFilterToVal < +heroFilterMin) { heroFilterTo.value = this.prettify(heroFilterMax); } else if (heroFilterToVal > +heroFilterMax) { heroFilterTo.value = this.prettify(heroFilterMax); } }); heroFilterReset.addEventListener('click', () => { heroFilterFields.forEach(field => { field.value = ''; }); heroFilterReset.classList.remove('active'); }); } }); document.addEventListener('click', (e) => { if (!e.target.closest('.js_hero_filter_dropdown') && !e.target.closest('.js_hero_filter_current')) { heroFilters.forEach(filter => { filter.classList.remove('active'); }); heroSearchBtns.forEach(btn => { btn.disabled = false; }); } }); } } // открытие/закрытие типовых поп-апов controlPopups() { const popupShowBtns = document.querySelectorAll('[data-btn]'); const popups = document.querySelectorAll('[data-popup]'); if (popupShowBtns.length) { popupShowBtns.forEach(btn => { btn.addEventListener('click', (e) => { e.preventDefault(); popups.forEach(popup => { popup.classList.remove('active'); // если какойто поп-ап открыт, то закрываем его; this.unfixBodyPosition(); if (btn.dataset.btn == popup.dataset.popup) { popup.classList.add('active'); this.fixBodyPosition(); } }); }); }); popups.forEach(popup => { const popupCloseBtns = popup.querySelectorAll('.js_popup_close'); popupCloseBtns.forEach(btn => { btn.addEventListener('click', (e) => { e.preventDefault(); popup.classList.remove('active'); this.unfixBodyPosition(); }); }); popup.addEventListener('click', (e) => { if (e.target == popup) { popup.classList.remove('active'); this.unfixBodyPosition(); } }); }); } } // открытие/закрытие поп-апа 'обратный звонок' controlContactUsPopup() { const contactUsBtn = document.querySelector('.js_btn_contact_us'); const contactUsPopup = document.querySelector('.js_contact_us'); if (contactUsPopup) { const contactUsPopupCloseBtns = contactUsPopup.querySelectorAll('.js_contact_us_close'); contactUsBtn.addEventListener('click', (e) => { e.preventDefault(); if (contactUsPopup.classList.contains('active')) { contactUsPopup.classList.remove('active'); } else { contactUsPopup.classList.add('active'); } }); contactUsPopupCloseBtns.forEach(btn => { btn.addEventListener('click', () => { contactUsPopup.classList.remove('active'); }); }); document.addEventListener('click', (e) => { if (!e.target.closest('.js_contact_us') && !e.target.closest('.js_btn_contact_us')) { contactUsPopup.classList.remove('active'); } }); } } // валидатор форм validateForm(input) { // функция добавления ошибки const createError = (text) => { input.classList.add('error'); input.classList.remove('no-error'); if (input.closest('label').querySelector('span.error')) { input.closest('label').querySelector('span.error').remove(); input.closest('label').insertAdjacentHTML('beforeend', `${text}`); } else { input.closest('label').insertAdjacentHTML('beforeend', `${text}`); } } // функция удаления ошибки const removeError = () => { input.classList.remove('error'); input.classList.add('no-error'); if (input.closest('label').querySelector('span.error')) { input.closest('label').querySelector('span.error').remove(); } } // проверяем на правильность заполнения поля 'Телефон' if (input.classList.contains('js_input_phone') && input.value == "") { createError('Заполните, пожалуйста, поле'); } else if (input.classList.contains('js_input_phone') && input.value.search(this.patternPhone) == 0) { removeError(); } else if (input.classList.contains('js_input_phone')) { createError('Укажите, пожалуйста, корректный телефон'); } // проверяем правильность заполнения поля 'Электронная почта' if (input.classList.contains('js_input_email') && input.value == "") { createError('Заполните, пожалуйста, поле'); } else if (input.classList.contains('js_input_email') && input.value.search(this.patternEmail) == 0) { removeError(); } else if (input.classList.contains('js_input_email')) { createError('Укажите, пожалуйста, корректный e-mail'); } } // отправка форм sendForm(formEl, success) { const form = document.querySelector(formEl); if (form) { form.addEventListener('submit', async (e) => { e.preventDefault(); const formInputs = form.querySelectorAll('input'); const formBtn = form.querySelector('.js_form_btn'); formInputs.forEach(input => { // перебираем все инпуты в форме; this.validateForm(input); input.addEventListener('input', () => { this.validateForm(input); }); }); if (!form.querySelector('.error')) { //проверяем, чтоб все инпуты прошли валидацию (чтоб не было в форме ни одного элемента с класссом error); // сюда пишем команды, которые должны сработать после успешной валидации; console.log('validate'); formBtn.classList.add('btn-animate'); formBtn.disabled = true; const formData = new FormData(form); console.log(...formData); const response = await fetch(e.target.action, { method: e.target.method, body: formData }); if (response.ok) { setTimeout(() => { // имитация отправки, когда отправка будет настроена, нужно достать всё из setTimeout() и удалить его; console.log('Отправлено'); formBtn.classList.remove('btn-animate'); formBtn.disabled = false; if (document.querySelector('[data-popup="feedback"]')) { document.querySelector('[data-popup="feedback"]').classList.remove('active'); } if (document.querySelector('[data-popup="viewing"]')) { document.querySelector('[data-popup="viewing"]').classList.remove('active'); } document.querySelector(success).classList.add('active'); this.fixBodyPosition(); form.reset(); formInputs.forEach(input => { input.classList.remove('no-error'); }); }, 2000) } else { formBtn.classList.remove('btn-animate'); formBtn.disabled = false; alert('Ошибка'); } } else { console.log('no-validate'); form.querySelector('.error').focus(); //фокус к полю с ошибкой; } }); } } //отправка предложения по e-mail sendOffer() { const form = document.querySelector('.js_popup_sending_form'); if (form) { form.addEventListener('submit', async (e) => { e.preventDefault(); const formInputs = form.querySelectorAll('input'); const formBtn = form.querySelector('.js_form_btn'); formInputs.forEach(input => { // перебираем все инпуты в форме; this.validateForm(input); input.addEventListener('input', () => { this.validateForm(input); }); }); if (!form.querySelector('.error')) { //проверяем, чтоб все инпуты прошли валидацию (чтоб не было в форме ни одного элемента с класссом error); // сюда пишем команды, которые должны сработать после успешной валидации; console.log('validate'); formBtn.classList.add('btn-animate'); formBtn.disabled = true; const formData = new FormData(form); console.log(...formData); const response = await fetch(e.target.action, { method: e.target.method, body: formData }); if (response.ok) { setTimeout(() => { // имитация отправки, когда отправка будет настроена, нужно достать всё из setTimeout() и удалить его; console.log('Отправлено'); formBtn.classList.remove('btn-animate'); formBtn.disabled = false; if (document.querySelector('[data-popup="sending"]')) { document.querySelector('[data-popup="sending"]').classList.remove('active'); } this.fixBodyPosition(); form.reset(); formInputs.forEach(input => { input.classList.remove('no-error'); }); }, 2000) } else { formBtn.classList.remove('btn-animate'); formBtn.disabled = false; alert('Ошибка'); } } else { console.log('no-validate'); form.querySelector('.error').focus(); //фокус к полю с ошибкой; } }); } } // карта на странице 'ЖК' setComplexMap(id, coords, caption) { if (document.querySelector('#' + id)) { // Дождёмся загрузки API и готовности DOM. ymaps.ready(init); function init() { const map = new ymaps.Map(id, { // При инициализации карты обязательно нужно указать её центр и коэффициент масштабирования. center: coords, zoom: 16, controls: [] }); // Создаём макет содержимого. const MyIconContentLayout = ymaps.templateLayoutFactory.createClass( '
$[properties.iconContent]
' ); // Создание макета содержимого хинта. // Макет создается через фабрику макетов с помощью текстового шаблона. const HintLayout = ymaps.templateLayoutFactory.createClass("
" + "{{ properties.object }}" + "
", { // Определяем метод getShape, который // будет возвращать размеры макета хинта. // Это необходимо для того, чтобы хинт автоматически // сдвигал позицию при выходе за пределы карты. getShape: function () { let el = this.getElement(), result = null; if (el) { var firstChild = el.firstChild; result = new ymaps.shape.Rectangle( new ymaps.geometry.pixel.Rectangle([ [0, 0], [firstChild.offsetWidth, firstChild.offsetHeight] ]) ); } return result; } } ); // метка const placemark = new ymaps.Placemark(coords, { // hintContent: caption, // balloonContent: caption, iconContent: '1', // address: caption, object: caption }, { iconLayout: 'default#imageWithContent', iconImageHref: 'images/mark-complex.svg', iconImageSize: [52, 67], iconImageOffset: [-26, -67], iconContentOffset: [0, 17], iconContentLayout: MyIconContentLayout, hintLayout: HintLayout }); map.geoObjects.add(placemark); } } } // фильтры и сортировка на странице 'каталог' setCatalogSorts() { const sortGroups = document.querySelectorAll('.js_sort_group'); if (sortGroups.length) { sortGroups.forEach(group => { const sortGroupInput = group.querySelector('.js_sort_group_input'); const sortGroupCurrent = group.querySelector('.js_sort_group_current'); const sortGroupList = group.querySelector('.js_sort_group_list'); const sortGroupItems = group.querySelectorAll('.js_sort_group_item'); const sendRequest = () => { const spinner = document.querySelector('.spinner'); // спиннер; spinner.classList.add('active'); document.body.classList.add('overlay'); /*this.fixBodyPosition(); fetch('test.json') .then(response => response.json()) .then(data => { console.log() setTimeout(() => { //имитация ответа сервера spinner.classList.remove('active'); document.body.classList.remove('overlay'); this.unfixBodyPosition(); }, 3000); }) .catch(err => { console.log(err); }); */ spinner.classList.remove('active'); document.body.classList.remove('overlay'); }; sortGroupCurrent.addEventListener('click', () => { if (group.classList.contains('active')) { group.classList.remove('active'); } else { sortGroups.forEach(group => { group.classList.remove('active'); }); group.classList.add('active'); } }); sortGroupItems.forEach(item => { item.addEventListener('click', () => { sortGroupItems.forEach(item => { item.classList.remove('active'); }); item.classList.add('active'); sortGroupCurrent.textContent = item.textContent; sortGroupInput.value = item.dataset.val; group.classList.remove('active'); sendRequest(); }); }); }); document.addEventListener('click', (e) => { if (!e.target.closest('.js_sort_group_list') && !e.target.closest('.js_sort_group_current')) { sortGroups.forEach(group => { group.classList.remove('active'); }); } }); } } // слайдер на странице жк и на странице предложения initIntroSlider() { let swiper3 = new Swiper('.intro__swiper', { navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev', }, pagination: { el: '.swiper-pagination', clickable: true, }, slidesPerView: 1.1, spaceBetween: 20, breakpoints: { 480: { slidesPerView: 1.5, }, 640: { slidesPerView: 1.75, }, 780: { slidesPerView: 2.15, }, 1024: { slidesPerView: 3.5, }, 1200: { slidesPerView: 1, } } }); } // табы на странице предложения setTabs(tabs, items) { const offerSideTabs = document.querySelectorAll(tabs); const offerSideItems = document.querySelectorAll(items); if (offerSideTabs) { offerSideTabs.forEach(tab => { tab.addEventListener('click', () => { offerSideTabs.forEach(tab => { tab.classList.remove('active'); }); tab.classList.add('active'); offerSideItems.forEach(item => { item.classList.remove('active', 'fade'); if (tab.dataset.tab == item.dataset.item) { item.classList.add('active', 'fade'); } }); }); }); } } // логика открытия нужного таба при открытии поп-апа с планами объекат и этажа на странице предложения sontrolOfferSidePopup() { const offerSideItems = document.querySelectorAll('.js_offer_side_item'); const offerSidePopupTabs = document.querySelectorAll('.js_offer_side_popup_tab'); const offerSidePopupItems = document.querySelectorAll('.js_offer_side_popup_item'); if (offerSideItems) { offerSideItems.forEach(item => { const offerSideItemBtn = item.querySelector('.js_offer_side_item_btn'); offerSideItemBtn.addEventListener('click', (e) => { e.preventDefault(); offerSidePopupTabs.forEach(tab => { tab.classList.remove('active'); if (item.dataset.item == tab.dataset.tab) { tab.classList.add('active'); } }); offerSidePopupItems.forEach(el => { el.classList.remove('active', 'fade'); if (item.dataset.item == el.dataset.item) { el.classList.add('active', 'fade'); } }); }); }); } } // галлерея setCustomGallery() { let swiper4 = new Swiper(".img-viewer__thumbs-swiper", { slidesPerView: 3, spaceBetween: 8, // freeMode: true, observer: true, observeParents: true, observeSlideChildren: true, breakpoints: { 640: { spaceBetween: 10, }, }, }); let swiper5 = new Swiper(".img-viewer__slider .swiper", { navigation: { nextEl: ".img-viewer__slider .swiper-button-next", prevEl: ".img-viewer__slider .swiper-button-prev", }, slidesPerView: 1, spaceBetween: 20, thumbs: { swiper: swiper4 }, observer: true, observeParents: true, observeSlideChildren: true, }); if (document.querySelector('.js_intro_item_btn')) { const imgViewer = document.querySelector('.js_img_viewer'); const imgViewerCloses = imgViewer.querySelectorAll('.js_img_viewer_close'); const imgViewerCaption = imgViewer.querySelector('.js_img_viewer_caption'); const imgViewerSliderSwiper = imgViewer.querySelector('.js_img_viewer_slider_swiper'); const imgViewerSliderSwiperWrap = imgViewerSliderSwiper.querySelector('.js_img_viewer_slider_swiper .swiper-wrapper'); const imgViewerThumbsSwiper = imgViewer.querySelector('.js_img_viewer_thumbs_swiper'); const imgViewerThumbsSwiperWrap = imgViewerThumbsSwiper.querySelector('.js_img_viewer_thumbs_swiper .swiper-wrapper'); const introItemBtns = document.querySelectorAll('.js_intro_item_btn'); introItemBtns.forEach((btn, i) => { btn.addEventListener('click', (e) => { e.preventDefault(); imgViewer.classList.add('active'); this.fixBodyPosition(); imgViewerSliderSwiperWrap.innerHTML = ''; imgViewerThumbsSwiperWrap.innerHTML = ''; imgViewerCaption.textContent = ''; introItemBtns.forEach(btn => { imgViewerSliderSwiperWrap.insertAdjacentHTML('beforeend', `
` ); imgViewerThumbsSwiperWrap.insertAdjacentHTML('beforeend', `
` ); }); swiper4.update(); swiper5.update(); swiper5.slideTo(i); imgViewerCaption.textContent = btn.dataset.caption; }); }); swiper5.on('slideChange', function () { imgViewerCaption.textContent = introItemBtns[swiper5.realIndex].dataset.caption; }); imgViewerCloses.forEach(close => { close.addEventListener('click', () => { imgViewer.classList.remove('active'); this.unfixBodyPosition(); }); }); } } // куки setCookies() { const cookies = document.querySelector('.js_cookies'); const cookiesBtn = document.querySelector('.js_cookies_confirm'); const cookiesTrigger = document.querySelector('.js_btn_cookies'); if (cookiesTrigger) { cookiesTrigger.addEventListener('click', () => { cookies.classList.add('active'); }); }; if (cookies) { cookiesBtn.addEventListener('click', () => { cookies.classList.remove('active'); }); }; } // карта на странице карт; setGeneralMap() { if (document.querySelector('#general-map')) { ymaps.ready(init); // Дождёмся загрузки API и готовности DOM; function init() { const myMap = new ymaps.Map('general-map', { // Создание экземпляра карты и его привязка к контейнеру с заданным id; center: [55.752933963675126, 37.52233749962665], // При инициализации карты обязательно нужно указать её центр и коэффициент масштабирования; zoom: 10, controls: [] // Скрываем элементы управления на карте; }); // Создаём макет содержимого. const MyIconContentLayout = ymaps.templateLayoutFactory.createClass( '
$[properties.iconContent]
' ); let collection = new ymaps.GeoObjectCollection(null, { // Создаём коллекцию, в которую будемпомещать метки (что-то типа массива); // preset: 'islands#yellowIcon' }); let collectionCoords = [ // Создаём массив с координатами (координаты должны располагаться в том же порядке, что и адреса в списке на сайте); [55.867783219108354, 37.392867499999916], [55.728043075486504, 37.73937949999994], [55.72624100423305, 37.476078499999964], [55.80751105044832, 37.449622999999974], [55.601783098948836, 37.36700499999998], [55.86086502152225, 37.540348999999964], [55.784961528728715, 37.56188599999996], [55.63910010399773, 37.319407999999996], [55.55819256767507, 37.55711549999994], [55.79829252928473, 37.52063549999999], ]; for (let i = 0, l = collectionCoords.length; i < l; i++) { // C помощью цикла добавляем все метки в коллекцию; collection.add(new ymaps.Placemark(collectionCoords[i])); collection.get(i).properties.set('iconContent', `${i + 1}`); // Добавляем каждой метке порядковый номер, записываем его в свойство 'iconContent'; } myMap.geoObjects.add(collection); // Добавляем коллекцию с метками на карту; collection.options.set('iconLayout', 'default#imageWithContent'); // Необходимо указать данный тип макета; collection.options.set('iconImageHref', 'images/mark-complex.svg'); // Своё изображение иконки метки; collection.options.set('iconImageSize', [52, 67]); // Размеры метки; collection.options.set('iconImageOffset', [-26, -67]); // Смещение левого верхнего угла иконки относительно её "ножки" (точки привязки); collection.options.set('iconContentOffset', [0, 17]); collection.options.set('iconContentLayout', MyIconContentLayout); // Смещение левого верхнего угла иконки относительно её "ножки" (точки привязки); const pageMapBar = document.querySelector('.js_page_map_bar'); const pageMapBarBtn = pageMapBar.querySelector('.js_page_map_bar_btn'); const pageMapBarList = pageMapBar.querySelector('.js_page_map_bar_list'); const pageMapBarCards = pageMapBar.querySelectorAll('.card-news'); const showCard = (i) => { pageMapBarCards.forEach((card, k) => { card.classList.remove('active'); if (i == k) { card.classList.add('active'); } }); }; const hidecard = () => { pageMapBarCards.forEach(card => { card.classList.remove('active'); }); } let pageMapBarItems; pageMapBarBtn.addEventListener('click', () => { pageMapBar.classList.toggle('active'); }); pageMapBarList.addEventListener('click', (e) => { if (e.target.closest('.page-map-bar__item')) { pageMapBarItems = pageMapBarList.querySelectorAll('.page-map-bar__item'); pageMapBarItems.forEach((item, i) => { if (e.target == item && e.target.classList.contains('active')) { item.classList.remove('active'); hidecard(); } else if (e.target == item) { pageMapBarItems.forEach(item => { item.classList.remove('active'); }); item.classList.add('active'); let offsetCoords = collection.get(i).geometry.getCoordinates(); offsetCoords = [ offsetCoords[0] - 0.0025, offsetCoords[1] ]; myMap.setZoom(16); // myMap.setCenter(collection.get(i).geometry.getCoordinates()); myMap.setCenter(offsetCoords); showCard(i); } }); } }); collection.events.add('click', function (e) { for (let i = 0, l = collection.getLength(); i < l; i++) { if (e.get('target') == collection.get(i)) { pageMapBarItems = pageMapBarList.querySelectorAll('.page-map-bar__item'); pageMapBarItems.forEach((item) => { pageMapBar.classList.add('active'); item.classList.remove('active'); }); pageMapBarItems[i].classList.add('active'); showCard(i); } } }); } } }; // аккордеон в футере setFooterSpoilers() { const items = document.querySelectorAll('.js_footer_col'); items.forEach(item => { const itemTitle = item.querySelector('.js_footer_caption'); const itemContent = item.querySelector('.js_footer_block'); const blockToggle = (block, duration) => { if (window.getComputedStyle(block).display == "none" && !block.classList.contains('smooth')) { block.style.display = "block"; const blockHeight = block.offsetHeight; block.style.height = 0; block.style.overflow = "hidden"; block.style.transition = `height ${duration}ms ease`; block.classList.add('smooth'); block.offsetHeight; block.style.height = `${blockHeight}px`; setTimeout(() => { block.classList.remove('smooth'); block.style.height = ''; block.style.transition = ''; block.style.overflow = ''; }, duration); } else if (!block.classList.contains('smooth')) { block.style.height = `${block.offsetHeight}px`; block.offsetHeight; block.style.height = 0; block.style.overflow = "hidden"; block.style.transition = `height ${duration}ms ease`; block.classList.add('smooth'); setTimeout(() => { block.classList.remove('smooth'); block.style.display = "none"; block.style.height = ''; block.style.transition = ''; block.style.overflow = ''; }, duration); } }; itemTitle.addEventListener('click', (e) => { itemTitle.classList.toggle('active'); blockToggle(itemContent, 300); }); }); } // слайдер с партнёрами; initPartnerslSlider() { const slider = document.querySelector('.partners__swiper'); if (slider) { let swiper6; const initSlider = () => { swiper6 = new Swiper(slider, { // scrollbar: { // el: '.swiper-scrollbar', // draggable: true, // }, slidesPerView: 0.275, loop: true, spaceBetween: 20, freeMode: true, allowTouchMove: true, breakpoints: { 480: { slidesPerView: 0.4, }, 640: { slidesPerView: 0.65, }, 780: { slidesPerView: 0.65, }, 1024: { slidesPerView: 0.8, }, 1200: { slidesPerView: 1, loop: false, allowTouchMove: false, } } }); }; initSlider(); const updateSlider = () => { swiper6.destroy(); initSlider(); } window.addEventListener('resize', () => { if (window.innerWidth <= 1200 && slider.dataset.mobile == 'false') { slider.dataset.mobile = 'true'; updateSlider(); } if (window.innerWidth > 1200 && slider.dataset.mobile == 'true') { slider.dataset.mobile = 'false'; updateSlider(); } }); } } } document.addEventListener('DOMContentLoaded', () => { const app = new App(); app.init(); });