Files
goodbuddy/sites/app.js
T

127 lines
3.9 KiB
JavaScript

(() => {
"use strict";
const root = document.documentElement;
const header = document.querySelector("[data-site-header]");
const menuToggle = document.querySelector("[data-menu-toggle]");
const navigation = document.querySelector("[data-navigation]");
const themeToggle = document.querySelector("[data-theme-toggle]");
const themeColor = document.querySelector('meta[name="theme-color"]');
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)");
const getSavedTheme = () => {
try {
const savedTheme = localStorage.getItem("goodbuddy-site-theme");
return savedTheme === "light" || savedTheme === "dark" ? savedTheme : null;
} catch {
return null;
}
};
const applyTheme = (theme, persist = false) => {
root.dataset.theme = theme;
themeToggle?.setAttribute(
"aria-label",
theme === "dark" ? "切换为浅色主题" : "切换为深色主题",
);
themeColor?.setAttribute("content", theme === "dark" ? "#07101f" : "#f6f8fb");
if (persist) {
try {
localStorage.setItem("goodbuddy-site-theme", theme);
} catch {
// The selected theme still applies for the current page.
}
}
};
const closeMenu = () => {
header?.classList.remove("is-menu-open");
menuToggle?.setAttribute("aria-expanded", "false");
menuToggle?.setAttribute("aria-label", "打开导航");
};
const setHeaderState = () => {
header?.classList.toggle("is-scrolled", window.scrollY > 12);
};
applyTheme(getSavedTheme() ?? (systemTheme.matches ? "dark" : "light"));
setHeaderState();
themeToggle?.addEventListener("click", () => {
applyTheme(root.dataset.theme === "dark" ? "light" : "dark", true);
});
systemTheme.addEventListener("change", (event) => {
if (!getSavedTheme()) {
applyTheme(event.matches ? "dark" : "light");
}
});
menuToggle?.addEventListener("click", () => {
const willOpen = !header?.classList.contains("is-menu-open");
header?.classList.toggle("is-menu-open", willOpen);
menuToggle.setAttribute("aria-expanded", String(willOpen));
menuToggle.setAttribute("aria-label", willOpen ? "关闭导航" : "打开导航");
});
navigation?.addEventListener("click", (event) => {
if (event.target instanceof HTMLAnchorElement) {
closeMenu();
}
});
document.addEventListener("keydown", (event) => {
if (event.key === "Escape" && header?.classList.contains("is-menu-open")) {
closeMenu();
menuToggle?.focus();
}
});
document.addEventListener("click", (event) => {
if (
header?.classList.contains("is-menu-open") &&
event.target instanceof Node &&
!header.contains(event.target)
) {
closeMenu();
}
});
window.addEventListener("scroll", setHeaderState, { passive: true });
const sections = [...document.querySelectorAll("main section[id]")];
const navLinks = [...document.querySelectorAll('.site-navigation a[href^="#"]')];
if ("IntersectionObserver" in window) {
const observer = new IntersectionObserver(
(entries) => {
const visibleSection = entries
.filter((entry) => entry.isIntersecting)
.sort((left, right) => right.intersectionRatio - left.intersectionRatio)[0];
if (!visibleSection) {
return;
}
navLinks.forEach((link) => {
const isCurrent = link.getAttribute("href") === `#${visibleSection.target.id}`;
if (isCurrent) {
link.setAttribute("aria-current", "true");
} else {
link.removeAttribute("aria-current");
}
});
},
{ rootMargin: "-25% 0px -55%", threshold: [0.05, 0.2, 0.5] },
);
sections.forEach((section) => observer.observe(section));
}
const currentYear = document.querySelector("[data-current-year]");
if (currentYear) {
currentYear.textContent = String(new Date().getFullYear());
}
})();