refactor: cleaned up and optimized JS
This commit is contained in:
parent
d549590c1f
commit
69ff668926
1 changed files with 159 additions and 150 deletions
|
|
@ -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)) {
|
const navbar = document.querySelector('.navbar');
|
||||||
try {
|
const featureCards = document.querySelectorAll('.feature-card');
|
||||||
// Simulate API call - replace with your actual endpoint
|
const newsletterCards = document.querySelectorAll('.newsletter-card');
|
||||||
const response = await fetch("/subscribe", {
|
const progressBar = document.querySelector('.reading-progress');
|
||||||
method: "POST",
|
const emailInput = document.getElementById('email-input');
|
||||||
headers: { "Content-Type": "application/json" },
|
const notifyBtn = document.getElementById('notify-button');
|
||||||
body: JSON.stringify({ email }),
|
const emailRE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||||
});
|
|
||||||
|
|
||||||
const result = await response.json();
|
document.addEventListener(
|
||||||
alert(result.message || "Thank you for subscribing!");
|
'click',
|
||||||
emailInput.value = "";
|
(e) => {
|
||||||
} catch (error) {
|
const a = e.target.closest('a[href^="#"]');
|
||||||
console.error("Error during subscribe fetch:", error);
|
if (!a) return;
|
||||||
alert("Thank you! We'll notify you when we launch.");
|
|
||||||
emailInput.value = "";
|
const href = a.getAttribute('href');
|
||||||
|
if (!href || href === '#') return;
|
||||||
|
|
||||||
|
const target = document.querySelector(href);
|
||||||
|
if (!target) return;
|
||||||
|
|
||||||
|
e.preventDefault();
|
||||||
|
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
|
},
|
||||||
|
{ passive: false }
|
||||||
|
);
|
||||||
|
|
||||||
|
if ('IntersectionObserver' in window) {
|
||||||
|
const observer = new IntersectionObserver(
|
||||||
|
(entries, obs) => {
|
||||||
|
for (const entry of entries) {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
entry.target.classList.add('is-visible');
|
||||||
|
obs.unobserve(entry.target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
},
|
||||||
alert("Please enter a valid email address.");
|
{ threshold: 0.1, rootMargin: '0px 0px -50px 0px' }
|
||||||
}
|
);
|
||||||
});
|
|
||||||
|
|
||||||
// Smooth navbar background on scroll
|
featureCards.forEach((card) => {
|
||||||
window.addEventListener('scroll', function() {
|
card.classList.add('will-animate');
|
||||||
const navbar = document.querySelector('.navbar');
|
observer.observe(card);
|
||||||
if (window.scrollY > 100) {
|
|
||||||
navbar.style.background = 'rgba(255, 255, 255, 0.98)';
|
|
||||||
navbar.style.boxShadow = '0 2px 20px rgba(0, 0, 0, 0.1)';
|
|
||||||
} else {
|
|
||||||
navbar.style.background = 'rgba(255, 255, 255, 0.95)';
|
|
||||||
navbar.style.boxShadow = 'none';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add animation on scroll for feature cards
|
|
||||||
const observerOptions = {
|
|
||||||
threshold: 0.1,
|
|
||||||
rootMargin: '0px 0px -50px 0px'
|
|
||||||
};
|
|
||||||
|
|
||||||
const observer = new IntersectionObserver(function(entries) {
|
|
||||||
entries.forEach(entry => {
|
|
||||||
if (entry.isIntersecting) {
|
|
||||||
entry.target.style.opacity = '1';
|
|
||||||
entry.target.style.transform = 'translateY(0)';
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}, observerOptions);
|
} else {
|
||||||
|
featureCards.forEach((card) => card.classList.add('is-visible'));
|
||||||
|
}
|
||||||
|
|
||||||
// Initially hide feature cards for animation
|
window.addEventListener('load', () => {
|
||||||
document.querySelectorAll('.feature-card').forEach(card => {
|
document
|
||||||
card.style.opacity = '0';
|
.querySelectorAll('.newsletter-header, .newsletter-content')
|
||||||
card.style.transform = 'translateY(30px)';
|
.forEach((el, i) => {
|
||||||
card.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
|
el.style.transitionDelay = `${i * 0.2}s`;
|
||||||
observer.observe(card);
|
el.classList.add('is-visible');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add smooth scroll behavior for navigation
|
newsletterCards.forEach((card, i) => {
|
||||||
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
card.style.transitionDelay = `${i * 0.1}s`;
|
||||||
anchor.addEventListener('click', function (e) {
|
card.classList.add('is-visible');
|
||||||
e.preventDefault();
|
|
||||||
document.querySelector(this.getAttribute('href')).scrollIntoView({
|
|
||||||
behavior: 'smooth'
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add loading states for newsletter cards
|
let lastY = 0;
|
||||||
window.addEventListener('load', function() {
|
let ticking = false;
|
||||||
const cards = document.querySelectorAll('.newsletter-card');
|
|
||||||
cards.forEach((card, index) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
card.style.opacity = '1';
|
|
||||||
card.style.transform = 'translateY(0)';
|
|
||||||
}, index * 100);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Enhanced navbar scroll effect
|
function onScroll() {
|
||||||
window.addEventListener('scroll', function() {
|
lastY = window.scrollY || window.pageYOffset;
|
||||||
const navbar = document.querySelector('.navbar');
|
if (!ticking) {
|
||||||
if (window.scrollY > 50) {
|
requestAnimationFrame(updateOnScroll);
|
||||||
navbar.style.background = 'rgba(255, 255, 255, 0.98)';
|
ticking = true;
|
||||||
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)';
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
// Share functionality
|
function updateOnScroll() {
|
||||||
function shareNewsletter() {
|
if (navbar) {
|
||||||
if (navigator.share) {
|
navbar.classList.toggle('navbar--scrolled', lastY > 50);
|
||||||
navigator.share({
|
navbar.classList.toggle('navbar--deeper', lastY > 100);
|
||||||
title: document.title,
|
|
||||||
text: 'Check out this newsletter from RideAware',
|
|
||||||
url: window.location.href
|
|
||||||
}).catch(console.error);
|
|
||||||
} else {
|
|
||||||
// Fallback to copying URL to clipboard
|
|
||||||
navigator.clipboard.writeText(window.location.href).then(() => {
|
|
||||||
alert('Newsletter URL copied to clipboard!');
|
|
||||||
}).catch(() => {
|
|
||||||
alert('Unable to share. Please copy the URL manually.');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
const anchors = document.querySelectorAll('.newsletter-content a[href^="#"]');
|
|
||||||
anchors.forEach(anchor => {
|
|
||||||
anchor.addEventListener('click', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const target = document.querySelector(this.getAttribute('href'));
|
|
||||||
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) {
|
if (progressBar) {
|
||||||
progressBar.style.width = (progress * 100) + '%';
|
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}%`;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// Add animation delay for content loading
|
ticking = false;
|
||||||
window.addEventListener('load', function() {
|
}
|
||||||
const elements = document.querySelectorAll('.newsletter-header, .newsletter-content');
|
|
||||||
elements.forEach((element, index) => {
|
window.addEventListener('scroll', onScroll, { passive: true });
|
||||||
setTimeout(() => {
|
updateOnScroll(); // initial state
|
||||||
element.style.opacity = '1';
|
|
||||||
element.style.transform = 'translateY(0)';
|
if (notifyBtn && emailInput) {
|
||||||
}, index * 200);
|
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) {
|
||||||
|
await navigator.share({
|
||||||
|
title: document.title,
|
||||||
|
text: 'Check out this newsletter from RideAware',
|
||||||
|
url: location.href,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} 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!');
|
||||||
|
return;
|
||||||
|
} catch {
|
||||||
|
/* fall through */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const tmp = document.createElement('input');
|
||||||
|
tmp.value = location.href;
|
||||||
|
document.body.appendChild(tmp);
|
||||||
|
tmp.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
document.body.removeChild(tmp);
|
||||||
|
alert('Newsletter URL copied to clipboard!');
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue