106 lines
3.7 KiB
JavaScript
106 lines
3.7 KiB
JavaScript
// Stackless — contact form behaviour: step nav, validation, submit state
|
|
(function () {
|
|
var form = document.getElementById('contactForm');
|
|
if (!form) return;
|
|
|
|
var steps = Array.prototype.slice.call(form.querySelectorAll('.step'));
|
|
var dots = Array.prototype.slice.call(document.querySelectorAll('.progress .dot'));
|
|
var prevBtn = document.getElementById('prevBtn');
|
|
var nextBtn = document.getElementById('nextBtn');
|
|
var submitBtn = document.getElementById('submitBtn');
|
|
var successEl = document.getElementById('formSuccess');
|
|
var errBanner = document.getElementById('errBanner');
|
|
|
|
var current = 0;
|
|
|
|
function paint() {
|
|
steps.forEach(function (s, i) { s.classList.toggle('is-active', i === current); });
|
|
dots.forEach(function (d, i) {
|
|
d.classList.toggle('is-on', i === current);
|
|
d.classList.toggle('is-done', i < current);
|
|
});
|
|
prevBtn.disabled = current === 0;
|
|
if (current === steps.length - 1) {
|
|
nextBtn.style.display = 'none';
|
|
submitBtn.style.display = 'inline-flex';
|
|
} else {
|
|
nextBtn.style.display = 'inline-flex';
|
|
submitBtn.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
function validateStep(idx) {
|
|
var step = steps[idx];
|
|
var fields = step.querySelectorAll('input[required], select[required], textarea[required]');
|
|
var ok = true;
|
|
fields.forEach(function (el) {
|
|
var bad = !el.checkValidity();
|
|
el.classList.toggle('is-invalid', bad);
|
|
el.parentElement.classList.toggle('is-invalid', bad);
|
|
if (bad) ok = false;
|
|
});
|
|
// Custom: at least one service checkbox picked if on step 1
|
|
if (idx === 0) {
|
|
var any = step.querySelectorAll('input[name="services"]:checked').length;
|
|
var servicesErr = document.getElementById('servicesErr');
|
|
if (any === 0) {
|
|
ok = false;
|
|
servicesErr.style.display = 'block';
|
|
} else {
|
|
servicesErr.style.display = 'none';
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
function goto(i) {
|
|
if (i > current && !validateStep(current)) {
|
|
errBanner.style.display = 'flex';
|
|
return;
|
|
}
|
|
errBanner.style.display = 'none';
|
|
current = Math.max(0, Math.min(steps.length - 1, i));
|
|
paint();
|
|
// scroll the active step into view (only on mobile-ish where steps stack)
|
|
var active = steps[current];
|
|
if (active && window.innerWidth < 720) {
|
|
active.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
}
|
|
}
|
|
|
|
// live-clear invalid state as the user fixes it
|
|
form.addEventListener('input', function (e) {
|
|
var t = e.target;
|
|
if (t.classList && t.classList.contains('is-invalid') && t.checkValidity()) {
|
|
t.classList.remove('is-invalid');
|
|
if (t.parentElement) t.parentElement.classList.remove('is-invalid');
|
|
}
|
|
if (t.name === 'services') {
|
|
var any = form.querySelectorAll('input[name="services"]:checked').length;
|
|
var servicesErr = document.getElementById('servicesErr');
|
|
if (any > 0 && servicesErr) servicesErr.style.display = 'none';
|
|
}
|
|
});
|
|
|
|
prevBtn.addEventListener('click', function () { goto(current - 1); });
|
|
nextBtn.addEventListener('click', function () { goto(current + 1); });
|
|
|
|
// Submit (no real backend — show the success state, log the payload)
|
|
form.addEventListener('submit', function (e) {
|
|
e.preventDefault();
|
|
if (!validateStep(current)) { errBanner.style.display = 'flex'; return; }
|
|
var data = {};
|
|
new FormData(form).forEach(function (v, k) { data[k] = v; });
|
|
console.log('[Stackless] contact submission:', data);
|
|
|
|
submitBtn.disabled = true;
|
|
submitBtn.textContent = 'Sending…';
|
|
setTimeout(function () {
|
|
form.style.display = 'none';
|
|
successEl.style.display = 'block';
|
|
successEl.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
}, 700);
|
|
});
|
|
|
|
paint();
|
|
})();
|