Files
olimparena/assets/js/main.js
2026-05-15 14:29:01 +03:00

161 lines
4.7 KiB
JavaScript

(() => {
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
const selectors = [
".section__head",
".info-card",
".feature-card",
".object-card",
".service-card",
".gallery__item",
".review-card",
".stats article",
".booking__form",
".contacts-card",
".hours-card",
];
const elements = Array.from(document.querySelectorAll(selectors.join(",")));
if (!elements.length) return;
const siblingCounters = new WeakMap();
elements.forEach((element) => {
element.classList.add("scroll-reveal");
const parent = element.parentElement;
let offsetIndex = 0;
if (parent) {
offsetIndex = siblingCounters.get(parent) ?? 0;
siblingCounters.set(parent, offsetIndex + 1);
}
element.style.setProperty("--reveal-delay", `${(offsetIndex % 4) * 80}ms`);
});
if (reduceMotion || !("IntersectionObserver" in window)) {
elements.forEach((element) => element.classList.add("is-visible"));
return;
}
const observer = new IntersectionObserver(
(entries, currentObserver) => {
entries.forEach((entry) => {
if (!entry.isIntersecting) return;
entry.target.classList.add("is-visible");
currentObserver.unobserve(entry.target);
});
},
{
threshold: 0.18,
rootMargin: "0px 0px -12% 0px"
}
);
elements.forEach((element) => observer.observe(element));
})();
(() => {
const bookingForm = document.getElementById("booking-form");
const successModal = document.getElementById("booking-success-modal");
if (!bookingForm || !successModal) return;
const closeTriggers = Array.from(successModal.querySelectorAll("[data-modal-close]"));
const openModal = () => {
successModal.classList.add("is-open");
successModal.setAttribute("aria-hidden", "false");
};
const closeModal = () => {
successModal.classList.remove("is-open");
successModal.setAttribute("aria-hidden", "true");
};
const triggerAnalytics = () => {
const eventName = "заявка";
const metrikaIdRaw = bookingForm.dataset.metrikaId || document.body.dataset.metrikaId || window.YANDEX_METRIKA_ID;
const metrikaId = Number(metrikaIdRaw);
if (typeof window.ym === "function" && Number.isFinite(metrikaId) && metrikaId > 0) {
window.ym(metrikaId, "reachGoal", eventName);
}
const legacyCounterKey = Object.keys(window).find((key) => {
return key.startsWith("yaCounter") && typeof window[key]?.reachGoal === "function";
});
if (legacyCounterKey) {
window[legacyCounterKey].reachGoal(eventName);
}
if (typeof window.gtag === "function") {
window.gtag("event", eventName, {
event_category: "lead",
event_label: "booking_form"
});
}
if (Array.isArray(window.dataLayer)) {
window.dataLayer.push({
event: eventName,
event_category: "lead",
event_label: "booking_form"
});
}
};
const triggerAutoReply = async (email) => {
if (!email) return;
const autoReplyEndpoint = bookingForm.dataset.autoreplyEndpoint || window.BOOKING_AUTOREPLY_ENDPOINT;
if (!autoReplyEndpoint) return;
try {
await fetch(autoReplyEndpoint, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
email,
source: "booking-form"
})
});
} catch (error) {
// Auto-reply is optional and should not block form success.
console.error("Auto-reply request failed", error);
}
};
bookingForm.addEventListener("submit", async (event) => {
event.preventDefault();
if (!bookingForm.checkValidity()) {
bookingForm.reportValidity();
return;
}
const formData = new FormData(bookingForm);
const email = String(formData.get("email") || "").trim();
triggerAnalytics();
await triggerAutoReply(email);
bookingForm.reset();
openModal();
});
closeTriggers.forEach((trigger) => {
trigger.addEventListener("click", closeModal);
});
document.addEventListener("keydown", (event) => {
if (event.key === "Escape" && successModal.classList.contains("is-open")) {
closeModal();
}
});
})();