- Updated version number in version.txt. - Improved smooth scrolling for anchor links in script.js. - Added Intersection Observer for fade-in animations in script.js. - Enhanced CSS styles for various elements, including buttons, metrics, and backgrounds for better visual appeal and interactivity.
118 lines
3.4 KiB
JavaScript
118 lines
3.4 KiB
JavaScript
document.addEventListener('DOMContentLoaded', () => {
|
|
const navToggle = document.querySelector('.nav__toggle');
|
|
const navLinks = document.querySelector('.nav__links');
|
|
const yearEl = document.getElementById('year');
|
|
|
|
if (yearEl) {
|
|
yearEl.textContent = new Date().getFullYear();
|
|
}
|
|
|
|
if (navToggle && navLinks) {
|
|
const setExpanded = (isOpen) => {
|
|
navLinks.classList.toggle('is-open', isOpen);
|
|
navToggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
|
|
};
|
|
|
|
navToggle.addEventListener('click', () => {
|
|
const nextState = !navLinks.classList.contains('is-open');
|
|
setExpanded(nextState);
|
|
});
|
|
|
|
navLinks.addEventListener('click', (event) => {
|
|
if (event.target.closest('a')) {
|
|
setExpanded(false);
|
|
}
|
|
});
|
|
|
|
document.addEventListener('keydown', (event) => {
|
|
if (event.key === 'Escape' && navLinks.classList.contains('is-open')) {
|
|
setExpanded(false);
|
|
navToggle.focus();
|
|
}
|
|
});
|
|
}
|
|
|
|
document.querySelectorAll('[data-copy]').forEach((button) => {
|
|
const defaultLabel = button.textContent;
|
|
const setStatus = (label) => {
|
|
button.textContent = label;
|
|
setTimeout(() => {
|
|
button.textContent = defaultLabel;
|
|
}, 1800);
|
|
};
|
|
|
|
button.addEventListener('click', () => {
|
|
const target = document.querySelector(button.dataset.copy);
|
|
if (!target) {
|
|
return;
|
|
}
|
|
const textToCopy = target.textContent.trim();
|
|
const clipboard = navigator.clipboard;
|
|
|
|
if (clipboard && typeof clipboard.writeText === 'function') {
|
|
clipboard.writeText(textToCopy).then(() => {
|
|
setStatus('Copied!');
|
|
}).catch(() => {
|
|
setStatus('Copy manually');
|
|
});
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const range = document.createRange();
|
|
range.selectNodeContents(target);
|
|
const selection = window.getSelection();
|
|
selection.removeAllRanges();
|
|
selection.addRange(range);
|
|
const successful = document.execCommand('copy');
|
|
selection.removeAllRanges();
|
|
setStatus(successful ? 'Copied!' : 'Copy manually');
|
|
} catch (error) {
|
|
setStatus('Copy manually');
|
|
}
|
|
});
|
|
});
|
|
|
|
// Smooth scroll for anchor links
|
|
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
|
anchor.addEventListener('click', function (e) {
|
|
const href = this.getAttribute('href');
|
|
if (href === '#' || href === '#top') return;
|
|
|
|
e.preventDefault();
|
|
const target = document.querySelector(href);
|
|
if (target) {
|
|
target.scrollIntoView({
|
|
behavior: 'smooth',
|
|
block: 'start'
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
// Intersection Observer for fade-in animations
|
|
const observerOptions = {
|
|
threshold: 0.1,
|
|
rootMargin: '0px 0px -50px 0px'
|
|
};
|
|
|
|
const observer = new IntersectionObserver((entries) => {
|
|
entries.forEach(entry => {
|
|
if (entry.isIntersecting) {
|
|
entry.target.style.opacity = '1';
|
|
entry.target.style.transform = 'translateY(0)';
|
|
}
|
|
});
|
|
}, observerOptions);
|
|
|
|
// Observe sections and cards for fade-in effect (exclude hero section)
|
|
const animatedElements = document.querySelectorAll('.section:not(.hero), .card, .metric, figure, .install-steps li');
|
|
animatedElements.forEach(el => {
|
|
el.style.opacity = '0';
|
|
el.style.transform = 'translateY(20px)';
|
|
el.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
|
|
observer.observe(el);
|
|
});
|
|
|
|
});
|