refactor: cleaned up and optimized JS

This commit is contained in:
Cipher Vance 2025-08-24 13:17:47 -05:00
parent d549590c1f
commit 69ff668926

View file

@ -1,166 +1,175 @@
// Email subscription functionality (() => {
document.getElementById("notify-button").addEventListener("click", async () => { 'use strict';
const emailInput = document.getElementById("email-input");
const email = emailInput.value.trim();
if (email && /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
try {
// Simulate API call - replace with your actual endpoint
const response = await fetch("/subscribe", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email }),
});
const result = await response.json();
alert(result.message || "Thank you for subscribing!");
emailInput.value = "";
} catch (error) {
console.error("Error during subscribe fetch:", error);
alert("Thank you! We'll notify you when we launch.");
emailInput.value = "";
}
} else {
alert("Please enter a valid email address.");
}
});
// Smooth navbar background on scroll
window.addEventListener('scroll', function() {
const navbar = document.querySelector('.navbar'); const navbar = document.querySelector('.navbar');
if (window.scrollY > 100) { const featureCards = document.querySelectorAll('.feature-card');
navbar.style.background = 'rgba(255, 255, 255, 0.98)'; const newsletterCards = document.querySelectorAll('.newsletter-card');
navbar.style.boxShadow = '0 2px 20px rgba(0, 0, 0, 0.1)'; const progressBar = document.querySelector('.reading-progress');
} else { const emailInput = document.getElementById('email-input');
navbar.style.background = 'rgba(255, 255, 255, 0.95)'; const notifyBtn = document.getElementById('notify-button');
navbar.style.boxShadow = 'none'; const emailRE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
}
});
// Add animation on scroll for feature cards document.addEventListener(
const observerOptions = { 'click',
threshold: 0.1, (e) => {
rootMargin: '0px 0px -50px 0px' const a = e.target.closest('a[href^="#"]');
}; if (!a) return;
const observer = new IntersectionObserver(function(entries) { const href = a.getAttribute('href');
entries.forEach(entry => { if (!href || href === '#') return;
if (entry.isIntersecting) {
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}
});
}, observerOptions);
// Initially hide feature cards for animation const target = document.querySelector(href);
document.querySelectorAll('.feature-card').forEach(card => { if (!target) return;
card.style.opacity = '0';
card.style.transform = 'translateY(30px)';
card.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
observer.observe(card);
});
// Add smooth scroll behavior for navigation
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault(); e.preventDefault();
document.querySelector(this.getAttribute('href')).scrollIntoView({ target.scrollIntoView({ behavior: 'smooth', block: 'start' });
behavior: 'smooth' },
}); { passive: false }
}); );
});
// Add loading states for newsletter cards if ('IntersectionObserver' in window) {
window.addEventListener('load', function() { const observer = new IntersectionObserver(
const cards = document.querySelectorAll('.newsletter-card'); (entries, obs) => {
cards.forEach((card, index) => { for (const entry of entries) {
setTimeout(() => { if (entry.isIntersecting) {
card.style.opacity = '1'; entry.target.classList.add('is-visible');
card.style.transform = 'translateY(0)'; obs.unobserve(entry.target);
}, index * 100);
});
});
// Enhanced navbar scroll effect
window.addEventListener('scroll', function() {
const navbar = document.querySelector('.navbar');
if (window.scrollY > 50) {
navbar.style.background = 'rgba(255, 255, 255, 0.98)';
navbar.style.boxShadow = '0 5px 20px rgba(0, 0, 0, 0.1)';
} else {
navbar.style.background = 'rgba(255, 255, 255, 0.98)';
navbar.style.boxShadow = '0 2px 20px rgba(0, 0, 0, 0.05)';
} }
}); }
},
{ threshold: 0.1, rootMargin: '0px 0px -50px 0px' }
);
// Share functionality featureCards.forEach((card) => {
function shareNewsletter() { card.classList.add('will-animate');
observer.observe(card);
});
} else {
featureCards.forEach((card) => card.classList.add('is-visible'));
}
window.addEventListener('load', () => {
document
.querySelectorAll('.newsletter-header, .newsletter-content')
.forEach((el, i) => {
el.style.transitionDelay = `${i * 0.2}s`;
el.classList.add('is-visible');
});
newsletterCards.forEach((card, i) => {
card.style.transitionDelay = `${i * 0.1}s`;
card.classList.add('is-visible');
});
});
let lastY = 0;
let ticking = false;
function onScroll() {
lastY = window.scrollY || window.pageYOffset;
if (!ticking) {
requestAnimationFrame(updateOnScroll);
ticking = true;
}
}
function updateOnScroll() {
if (navbar) {
navbar.classList.toggle('navbar--scrolled', lastY > 50);
navbar.classList.toggle('navbar--deeper', lastY > 100);
}
if (progressBar) {
const max = document.body.scrollHeight - window.innerHeight;
const progress = max > 0 ? Math.min(Math.max(lastY / max, 0), 1) : 0;
progressBar.style.width = `${progress * 100}%`;
}
ticking = false;
}
window.addEventListener('scroll', onScroll, { passive: true });
updateOnScroll(); // initial state
if (notifyBtn && emailInput) {
let inFlight = false;
const controller = new AbortController();
notifyBtn.addEventListener('click', async () => {
const email = emailInput.value.trim();
if (!emailRE.test(email)) {
alert('Please enter a valid email address.');
emailInput.focus();
return;
}
if (inFlight) return;
inFlight = true;
notifyBtn.disabled = true;
try {
const res = await fetch('/subscribe', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email }),
signal: controller.signal,
});
let message = 'Thank you for subscribing!';
if (res.ok) {
const data = await res.json().catch(() => ({}));
message = data.message || message;
} else {
message = "Thanks! We'll notify you when we launch.";
}
alert(message);
emailInput.value = '';
} catch (err) {
console.error('Subscribe error:', err);
alert("Thanks! We'll notify you when we launch.");
emailInput.value = '';
} finally {
notifyBtn.disabled = false;
inFlight = false;
}
});
window.addEventListener('beforeunload', () => controller.abort(), {
passive: true,
});
}
window.shareNewsletter = async function shareNewsletter() {
try {
if (navigator.share) { if (navigator.share) {
navigator.share({ await navigator.share({
title: document.title, title: document.title,
text: 'Check out this newsletter from RideAware', text: 'Check out this newsletter from RideAware',
url: window.location.href url: location.href,
}).catch(console.error); });
} else { return;
// Fallback to copying URL to clipboard }
navigator.clipboard.writeText(window.location.href).then(() => { } catch (err) {
console.warn('navigator.share error/cancel:', err);
}
if (navigator.clipboard && window.isSecureContext) {
try {
await navigator.clipboard.writeText(location.href);
alert('Newsletter URL copied to clipboard!'); alert('Newsletter URL copied to clipboard!');
}).catch(() => { return;
alert('Unable to share. Please copy the URL manually.'); } catch {
}); /* fall through */
} }
}
// Enhanced navbar scroll effect
window.addEventListener('scroll', function() {
const navbar = document.querySelector('.navbar');
if (window.scrollY > 50) {
navbar.style.background = 'rgba(255, 255, 255, 0.98)';
navbar.style.boxShadow = '0 5px 20px rgba(0, 0, 0, 0.1)';
} else {
navbar.style.background = 'rgba(255, 255, 255, 0.98)';
navbar.style.boxShadow = '0 2px 20px rgba(0, 0, 0, 0.05)';
} }
});
// Smooth scroll for anchor links within newsletter content const tmp = document.createElement('input');
document.addEventListener('DOMContentLoaded', function() { tmp.value = location.href;
const anchors = document.querySelectorAll('.newsletter-content a[href^="#"]'); document.body.appendChild(tmp);
anchors.forEach(anchor => { tmp.select();
anchor.addEventListener('click', function(e) { document.execCommand('copy');
e.preventDefault(); document.body.removeChild(tmp);
const target = document.querySelector(this.getAttribute('href')); alert('Newsletter URL copied to clipboard!');
if (target) { };
target.scrollIntoView({ })();
behavior: 'smooth',
block: 'start'
});
}
});
});
});
// Add reading progress indicator
window.addEventListener('scroll', function() {
const article = document.querySelector('.newsletter-content');
const scrolled = window.scrollY;
const rate = scrolled / (document.body.scrollHeight - window.innerHeight);
const progress = Math.min(Math.max(rate, 0), 1);
// Update progress bar if exists
const progressBar = document.querySelector('.reading-progress');
if (progressBar) {
progressBar.style.width = (progress * 100) + '%';
}
});
// Add animation delay for content loading
window.addEventListener('load', function() {
const elements = document.querySelectorAll('.newsletter-header, .newsletter-content');
elements.forEach((element, index) => {
setTimeout(() => {
element.style.opacity = '1';
element.style.transform = 'translateY(0)';
}, index * 200);
});
});