Website-Entwurf für HEB Bauelemente (normtor.de)
Akquise-Demo mit Startseite (Tor-Intro, 24-h-Renovierung mit Uhr, Über uns, Fachbetrieb statt Discount), Galerie mit Filter und Großansicht, Kontaktseite mit Anfrageformular sowie Impressum- und Datenschutz-Entwürfen. Statisches HTML/CSS/JS, Seiten und Bilder werden über tools/ erzeugt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
bec24e5bbc
148 files changed
+2261
No files matched your search
+215
@@ -0,0 +1,215 @@
|
||||
/* HEB Bauelemente: Intro-Tor, Navigation, Einblendungen, 24-h-Uhr, Galerie, Formular. Ohne Bibliotheken. */
|
||||
(function () {
|
||||
"use strict";
|
||||
var root = document.documentElement;
|
||||
root.classList.add("ready");
|
||||
var reduced = matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||
|
||||
/* ---------- Intro: Sektionaltor fährt hoch (einmal pro Sitzung) ---------- */
|
||||
var gate = document.querySelector(".gate");
|
||||
if (gate && root.classList.contains("has-intro")) {
|
||||
try { sessionStorage.setItem("heb-intro", "1"); } catch (e) {}
|
||||
var started = false;
|
||||
var open = function () {
|
||||
if (started) return;
|
||||
started = true;
|
||||
gate.classList.add("is-moving");
|
||||
requestAnimationFrame(function () {
|
||||
gate.classList.add("is-open");
|
||||
root.classList.add("intro-go");
|
||||
});
|
||||
};
|
||||
gate.addEventListener("transitionend", function (e) {
|
||||
if (e.target === gate && e.propertyName === "transform") gate.remove();
|
||||
});
|
||||
gate.addEventListener("click", open);
|
||||
var img = document.querySelector(".hero__img img");
|
||||
setTimeout(function () {
|
||||
if (!img || img.complete) return open();
|
||||
img.addEventListener("load", open);
|
||||
setTimeout(open, 1200);
|
||||
}, 650);
|
||||
} else if (gate) {
|
||||
gate.remove();
|
||||
}
|
||||
|
||||
/* ---------- Header ---------- */
|
||||
var header = document.querySelector(".site-header");
|
||||
var onScroll = function () { header.classList.toggle("is-scrolled", scrollY > 8); };
|
||||
addEventListener("scroll", onScroll, { passive: true });
|
||||
onScroll();
|
||||
|
||||
var menuBtn = document.querySelector(".menu-btn");
|
||||
var mobileNav = document.getElementById("mobile-nav");
|
||||
if (menuBtn && mobileNav) {
|
||||
var setMenu = function (open) {
|
||||
menuBtn.setAttribute("aria-expanded", String(open));
|
||||
menuBtn.setAttribute("aria-label", open ? "Menü schließen" : "Menü öffnen");
|
||||
mobileNav.hidden = !open;
|
||||
};
|
||||
menuBtn.addEventListener("click", function () { setMenu(mobileNav.hidden); });
|
||||
mobileNav.addEventListener("click", function (e) { if (e.target.closest("a")) setMenu(false); });
|
||||
addEventListener("keydown", function (e) { if (e.key === "Escape" && !mobileNav.hidden) { setMenu(false); menuBtn.focus(); } });
|
||||
}
|
||||
|
||||
document.querySelectorAll("[data-year]").forEach(function (el) { el.textContent = new Date().getFullYear(); });
|
||||
|
||||
/* ---------- Einblendungen ---------- */
|
||||
var reveals = document.querySelectorAll(".reveal");
|
||||
if ("IntersectionObserver" in window && !reduced) {
|
||||
var io = new IntersectionObserver(function (entries) {
|
||||
entries.forEach(function (en) {
|
||||
if (en.isIntersecting) { en.target.classList.add("is-in"); io.unobserve(en.target); }
|
||||
});
|
||||
}, { threshold: 0.12, rootMargin: "0px 0px -40px 0px" });
|
||||
reveals.forEach(function (el) { io.observe(el); });
|
||||
} else {
|
||||
reveals.forEach(function (el) { el.classList.add("is-in"); });
|
||||
}
|
||||
|
||||
/* ---------- 24-h-Uhr: füllt sich mit den Arbeitsschritten ---------- */
|
||||
var steps = Array.prototype.slice.call(document.querySelectorAll(".step[data-hours]"));
|
||||
var bar = document.querySelector(".clock__bar");
|
||||
var num = document.querySelector("[data-clock]");
|
||||
if (steps.length && bar && num) {
|
||||
var shown = 0, target = 0, tween = null;
|
||||
var animateTo = function (h) {
|
||||
target = h;
|
||||
bar.style.strokeDashoffset = String(100 - (h / 24) * 100);
|
||||
if (reduced) { shown = h; num.textContent = h; return; }
|
||||
if (tween) return;
|
||||
tween = setInterval(function () {
|
||||
if (shown === target) { clearInterval(tween); tween = null; return; }
|
||||
shown += shown < target ? 1 : -1;
|
||||
num.textContent = shown;
|
||||
}, 45);
|
||||
};
|
||||
var ticking = false;
|
||||
var update = function () {
|
||||
ticking = false;
|
||||
var line = innerHeight * 0.6, active = -1;
|
||||
steps.forEach(function (s, i) { if (s.getBoundingClientRect().top < line) active = i; });
|
||||
steps.forEach(function (s, i) {
|
||||
s.classList.toggle("is-active", i === active);
|
||||
s.classList.toggle("is-done", i < active);
|
||||
});
|
||||
var h = active < 0 ? 0 : +steps[active].dataset.hours;
|
||||
if (h !== target) animateTo(h);
|
||||
};
|
||||
addEventListener("scroll", function () { if (!ticking) { ticking = true; requestAnimationFrame(update); } }, { passive: true });
|
||||
update();
|
||||
}
|
||||
|
||||
/* ---------- Galerie: Filter ---------- */
|
||||
var chips = document.querySelectorAll(".chip[data-filter]");
|
||||
var items = Array.prototype.slice.call(document.querySelectorAll(".gallery__item"));
|
||||
var count = document.querySelector("[data-count]");
|
||||
chips.forEach(function (chip) {
|
||||
chip.addEventListener("click", function () {
|
||||
var f = chip.dataset.filter, n = 0;
|
||||
chips.forEach(function (c) { c.setAttribute("aria-pressed", String(c === chip)); });
|
||||
items.forEach(function (it) {
|
||||
var show = f === "alle" || it.dataset.tags.split(" ").indexOf(f) > -1;
|
||||
it.hidden = !show;
|
||||
if (show) n++;
|
||||
});
|
||||
if (count) count.textContent = n;
|
||||
});
|
||||
});
|
||||
if (location.hash && items.length) {
|
||||
var hit = document.getElementById(location.hash.slice(1));
|
||||
if (hit && hit.classList.contains("gallery__item")) hit.classList.add("is-target");
|
||||
}
|
||||
|
||||
/* ---------- Galerie: Großansicht ---------- */
|
||||
var box = document.querySelector(".lightbox");
|
||||
if (box && typeof box.showModal === "function") {
|
||||
var bImg = box.querySelector("img"), bTitle = box.querySelector("figcaption b"), bSub = box.querySelector("figcaption span");
|
||||
var list = [], idx = 0, opener = null;
|
||||
var show = function (i) {
|
||||
idx = (i + list.length) % list.length;
|
||||
var b = list[idx];
|
||||
bImg.src = b.dataset.full;
|
||||
bImg.alt = b.dataset.title + ", " + b.dataset.sub;
|
||||
bTitle.textContent = b.dataset.title;
|
||||
bSub.textContent = b.dataset.sub;
|
||||
};
|
||||
document.querySelectorAll(".gallery__btn").forEach(function (btn) {
|
||||
btn.addEventListener("click", function () {
|
||||
list = items.filter(function (it) { return !it.hidden; }).map(function (it) { return it.querySelector(".gallery__btn"); });
|
||||
opener = btn;
|
||||
show(list.indexOf(btn));
|
||||
box.showModal();
|
||||
});
|
||||
});
|
||||
box.querySelector(".lightbox__close").addEventListener("click", function () { box.close(); });
|
||||
box.querySelector(".lightbox__prev").addEventListener("click", function () { show(idx - 1); });
|
||||
box.querySelector(".lightbox__next").addEventListener("click", function () { show(idx + 1); });
|
||||
box.addEventListener("click", function (e) { if (e.target === box) box.close(); });
|
||||
box.addEventListener("close", function () { if (opener) opener.focus(); });
|
||||
box.addEventListener("keydown", function (e) {
|
||||
if (e.key === "ArrowLeft") show(idx - 1);
|
||||
if (e.key === "ArrowRight") show(idx + 1);
|
||||
});
|
||||
var x0 = null;
|
||||
box.addEventListener("touchstart", function (e) { x0 = e.touches[0].clientX; }, { passive: true });
|
||||
box.addEventListener("touchend", function (e) {
|
||||
if (x0 === null) return;
|
||||
var dx = e.changedTouches[0].clientX - x0;
|
||||
if (Math.abs(dx) > 50) show(idx + (dx < 0 ? 1 : -1));
|
||||
x0 = null;
|
||||
});
|
||||
}
|
||||
|
||||
/* ---------- Anfrageformular ---------- */
|
||||
var form = document.getElementById("anfrage");
|
||||
if (form) {
|
||||
var pre = new URLSearchParams(location.search).get("anliegen");
|
||||
var preInput = pre && form.querySelector('input[name="anliegen"][value="' + pre.replace(/[^a-z]/g, "") + '"]');
|
||||
if (preInput) preInput.checked = true;
|
||||
|
||||
var setError = function (input, msg) {
|
||||
var wrap = input.closest(".field, .check");
|
||||
var old = wrap.querySelector(".field__error");
|
||||
if (old) old.remove();
|
||||
wrap.classList.toggle("is-invalid", !!msg);
|
||||
input.setAttribute("aria-invalid", msg ? "true" : "false");
|
||||
if (msg && wrap.classList.contains("field")) {
|
||||
var err = document.createElement("span");
|
||||
err.className = "field__error";
|
||||
err.id = "err-" + input.name;
|
||||
err.textContent = msg;
|
||||
wrap.appendChild(err);
|
||||
input.setAttribute("aria-describedby", err.id);
|
||||
} else {
|
||||
input.removeAttribute("aria-describedby");
|
||||
}
|
||||
};
|
||||
var check = function (input) {
|
||||
var v = input.type === "checkbox" ? input.checked : input.value.trim();
|
||||
var msg = "";
|
||||
if (input.required && !v) msg = input.type === "checkbox" ? "Bitte bestätigen." : "Bitte ausfüllen.";
|
||||
else if (input.name === "telefon" && v && v.replace(/\D/g, "").length < 6) msg = "Bitte eine gültige Telefonnummer angeben.";
|
||||
else if (input.type === "email" && v && !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(v)) msg = "Bitte eine gültige E-Mail-Adresse angeben.";
|
||||
setError(input, msg);
|
||||
return !msg;
|
||||
};
|
||||
var fields = form.querySelectorAll("input[required], input[type=email]");
|
||||
fields.forEach(function (f) {
|
||||
f.addEventListener(f.type === "checkbox" ? "change" : "blur", function () { check(f); });
|
||||
});
|
||||
form.addEventListener("submit", function (e) {
|
||||
e.preventDefault();
|
||||
var firstBad = null;
|
||||
fields.forEach(function (f) { if (!check(f) && !firstBad) firstBad = f; });
|
||||
if (firstBad) { firstBad.focus(); return; }
|
||||
var done = form.querySelector(".form__done");
|
||||
if (form.querySelector(".form__demo")) {
|
||||
done.querySelector("[data-done-text]").textContent = "Im Entwurf wird nichts verschickt. Nach dem Launch landet die Anfrage direkt bei Wolfgang Haas.";
|
||||
}
|
||||
form.querySelector("button[type=submit]").hidden = true;
|
||||
done.hidden = false;
|
||||
done.focus();
|
||||
});
|
||||
}
|
||||
})();
|
||||
Reference in new issue
Block a user