Zum Inhalt wechseln
.
*/
(function () {
"use strict";
// Run when DOM is ready and after Elementor renders
const onReady = (fn) => {
if (document.readyState !== "loading") fn();
else document.addEventListener("DOMContentLoaded", fn);
if (window.elementorFrontend && window.elementorFrontend.hooks) {
window.elementorFrontend.hooks.addAction("frontend/element_ready/global", fn);
}
};
function initSnap(container) {
if (!container || container.__snapInit) return;
container.__snapInit = true;
// Lock body scroll to avoid double scrollbars
document.body.classList.add("has-snap-wrap");
const panels = Array.from(container.children).filter(el => el.offsetParent !== null);
if (!panels.length) return;
// Ensure the container itself can receive keyboard focus (for Arrow/PageDown)
if (!container.hasAttribute("tabindex")) container.setAttribute("tabindex", "0");
let snapping = false;
let timer = null;
// Find nearest panel to current scrollTop
const nearest = () => {
const top = container.scrollTop;
let best = panels[0], dBest = Math.abs(panels[0].offsetTop - top);
for (let i = 1; i < panels.length; i++) {
const d = Math.abs(panels[i].offsetTop - top);
if (d < dBest) { best = panels[i]; dBest = d; }
}
return best;
};
const smoothTo = (y) => {
snapping = true;
container.scrollTo({ top: y, behavior: "smooth" });
// release after typical smooth duration
setTimeout(() => { snapping = false; }, 500);
};
// After user stops scrolling, gently land exactly on the closest panel
const schedule = () => {
if (snapping) return;
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
const t = nearest();
if (!t) return;
const delta = Math.abs(container.scrollTop - t.offsetTop);
if (delta > 1) smoothTo(t.offsetTop);
}, 120);
};
container.addEventListener("scroll", schedule, { passive: true });
// Keyboard nav feels great with snap
container.addEventListener("keydown", (e) => {
if (!container.contains(document.activeElement)) return;
const t = nearest();
const idx = panels.indexOf(t);
const go = (i) => {
if (i < 0 || i >= panels.length) return;
smoothTo(panels[i].offsetTop);
};
const code = e.code || e.key;
if (["PageDown", "ArrowDown", "ArrowRight", "Space"].includes(code)) { e.preventDefault(); go(idx + 1); }
else if (["PageUp", "ArrowUp", "ArrowLeft"].includes(code)) { e.preventDefault(); go(idx - 1); }
else if (code === "Home") { e.preventDefault(); go(0); }
else if (code === "End") { e.preventDefault(); go(panels.length - 1); }
});
// Keep panels matched to viewport on resize / mobile URL bar changes
const ro = new ResizeObserver(() => schedule());
ro.observe(container);
// Initial landing (in case the page opens mid-way)
schedule();
}
onReady(() => {
document.querySelectorAll(".snap-wrap").forEach(initSnap);
});
})();;