Regenerated site from SloanOps template
This commit is contained in:
+153
@@ -0,0 +1,153 @@
|
||||
// Mobile Menu Toggle
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const navToggle = document.querySelector('.nav-toggle');
|
||||
const navMenu = document.getElementById('nav-menu');
|
||||
|
||||
if (navToggle && navMenu) {
|
||||
navToggle.addEventListener('click', function() {
|
||||
const expanded = this.getAttribute('aria-expanded') === 'true' || false;
|
||||
this.setAttribute('aria-expanded', !expanded);
|
||||
navMenu.classList.toggle('active');
|
||||
});
|
||||
}
|
||||
|
||||
// Close mobile menu when clicking outside
|
||||
document.addEventListener('click', function(event) {
|
||||
if (navToggle && navMenu) {
|
||||
const isClickInsideNav = navToggle.contains(event.target) || navMenu.contains(event.target);
|
||||
if (!isClickInsideNav && navMenu.classList.contains('active')) {
|
||||
navToggle.setAttribute('aria-expanded', 'false');
|
||||
navMenu.classList.remove('active');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// FAQ Accordion
|
||||
const faqItems = document.querySelectorAll('.faq-item');
|
||||
faqItems.forEach(item => {
|
||||
const question = item.querySelector('.faq-question');
|
||||
if (question) {
|
||||
question.addEventListener('click', () => {
|
||||
const isActive = item.classList.contains('active');
|
||||
// Close all items
|
||||
faqItems.forEach(i => i.classList.remove('active'));
|
||||
// Open clicked item if it wasn't active
|
||||
if (!isActive) {
|
||||
item.classList.add('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Smooth scrolling for anchor links
|
||||
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||||
anchor.addEventListener('click', function(e) {
|
||||
const target = document.querySelector(this.getAttribute('href'));
|
||||
if (target) {
|
||||
e.preventDefault();
|
||||
target.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'start'
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Back to top button
|
||||
const backToTopButton = document.createElement('button');
|
||||
backToTopButton.innerHTML = '▲'; // Up arrow
|
||||
backToTopButton.className = 'back-to-top';
|
||||
backToTopButton.setAttribute('aria-label', 'Back to top');
|
||||
document.body.appendChild(backToTopButton);
|
||||
|
||||
backToTopButton.addEventListener('click', () => {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener('scroll', () => {
|
||||
if (window.pageYOffset > 300) {
|
||||
backToTopButton.classList.add('visible');
|
||||
} else {
|
||||
backToTopButton.classList.remove('visible');
|
||||
}
|
||||
});
|
||||
|
||||
// Send contact messages to the configured n8n webhook without exposing any mail credentials.
|
||||
const forms = document.querySelectorAll('.contact-form');
|
||||
forms.forEach(form => {
|
||||
form.addEventListener('submit', async function(e) {
|
||||
e.preventDefault();
|
||||
const formNote = this.querySelector('.form-note');
|
||||
const submitButton = this.querySelector('button[type="submit"]');
|
||||
if (submitButton) submitButton.disabled = true;
|
||||
if (formNote) formNote.textContent = 'Sending…';
|
||||
|
||||
try {
|
||||
const response = await fetch(this.action, {
|
||||
method: 'POST',
|
||||
body: new FormData(this),
|
||||
headers: { 'Accept': 'application/json' }
|
||||
});
|
||||
if (!response.ok) throw new Error('Webhook submission failed');
|
||||
if (formNote) {
|
||||
formNote.textContent = 'Thank you for your message! We will get back to you soon.';
|
||||
formNote.style.color = '#10b981';
|
||||
}
|
||||
this.reset();
|
||||
} catch (error) {
|
||||
if (formNote) {
|
||||
formNote.textContent = 'We could not send your message. Please try again or contact us directly.';
|
||||
formNote.style.color = '#dc2626';
|
||||
}
|
||||
} finally {
|
||||
if (submitButton) submitButton.disabled = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Tab functionality for product categories
|
||||
const tabButtons = document.querySelectorAll('.category-tab');
|
||||
tabButtons.forEach(button => {
|
||||
button.addEventListener('click', () => {
|
||||
const tabSlug = button.getAttribute('aria-controls');
|
||||
const tabPanel = document.getElementById(tabSlug);
|
||||
|
||||
// Remove active class from all tabs and panels
|
||||
tabButtons.forEach(btn => btn.classList.remove('active'));
|
||||
document.querySelectorAll('.category-panel').forEach(panel => panel.classList.remove('active'));
|
||||
|
||||
// Add active class to clicked tab and corresponding panel
|
||||
button.classList.add('active');
|
||||
if (tabPanel) {
|
||||
tabPanel.classList.add('active');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Header scroll effect
|
||||
const header = document.querySelector('.site-header');
|
||||
let lastScroll = 0;
|
||||
|
||||
window.addEventListener('scroll', () => {
|
||||
const currentScroll = window.pageYOffset;
|
||||
|
||||
if (currentScroll <= 0) {
|
||||
header.classList.remove('scroll-up');
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentScroll > lastScroll && !header.classList.contains('scroll-down')) {
|
||||
// Scrolling down
|
||||
header.classList.remove('scroll-up');
|
||||
header.classList.add('scroll-down');
|
||||
} else if (currentScroll < lastScroll && header.classList.contains('scroll-down')) {
|
||||
// Scrolling up
|
||||
header.classList.remove('scroll-down');
|
||||
header.classList.add('scroll-up');
|
||||
}
|
||||
lastScroll = currentScroll;
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user