修复主题切换按钮
This commit is contained in:
parent
4bb37c4a4d
commit
fdbc983d72
@ -288,8 +288,6 @@ const normalizedPath =
|
|||||||
<script>
|
<script>
|
||||||
// Header组件逻辑 - 使用"进入→绑定→退出完全清理"模式
|
// Header组件逻辑 - 使用"进入→绑定→退出完全清理"模式
|
||||||
(function() {
|
(function() {
|
||||||
// 生成唯一ID用于日志跟踪
|
|
||||||
const headerId = 'header-' + (new Date().getTime());
|
|
||||||
|
|
||||||
// 存储所有事件监听器,便于统一清理
|
// 存储所有事件监听器,便于统一清理
|
||||||
const listeners: Array<{
|
const listeners: Array<{
|
||||||
|
@ -99,119 +99,135 @@ const {
|
|||||||
// 确保当前没有活动的主题切换按钮事件
|
// 确保当前没有活动的主题切换按钮事件
|
||||||
cleanup();
|
cleanup();
|
||||||
|
|
||||||
// 获取所有主题切换按钮
|
// 添加延时确保DOM已准备好
|
||||||
const themeToggleButtons = document.querySelectorAll('#theme-toggle-button');
|
setTimeout(() => {
|
||||||
|
// 获取所有主题切换按钮
|
||||||
if (!themeToggleButtons.length) {
|
const themeToggleButtons = document.querySelectorAll('#theme-toggle-button');
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let transitioning = false;
|
|
||||||
|
|
||||||
// 获取系统首选主题
|
|
||||||
const getSystemTheme = (): string => {
|
|
||||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
||||||
};
|
|
||||||
|
|
||||||
// 初始化主题
|
|
||||||
const initializeTheme = (): void => {
|
|
||||||
const storedTheme = localStorage.getItem('theme');
|
|
||||||
const systemTheme = getSystemTheme();
|
|
||||||
|
|
||||||
// 按照逻辑优先级应用主题
|
if (!themeToggleButtons.length) {
|
||||||
if (storedTheme) {
|
// 如果找不到按钮,再尝试一次延迟初始化
|
||||||
document.documentElement.dataset.theme = storedTheme;
|
setTimeout(setupThemeToggle, 50);
|
||||||
} else if (systemTheme) {
|
|
||||||
document.documentElement.dataset.theme = systemTheme;
|
|
||||||
} else {
|
|
||||||
document.documentElement.dataset.theme = 'light';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 切换主题
|
|
||||||
const toggleTheme = (): void => {
|
|
||||||
if (transitioning) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
transitioning = true;
|
let transitioning = false;
|
||||||
|
|
||||||
// 获取当前主题
|
// 获取系统首选主题
|
||||||
const currentTheme = document.documentElement.dataset.theme;
|
const getSystemTheme = (): string => {
|
||||||
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
|
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||||
|
};
|
||||||
// 更新 HTML 属性
|
|
||||||
document.documentElement.dataset.theme = newTheme;
|
|
||||||
|
|
||||||
// 更新本地存储
|
|
||||||
const systemTheme = getSystemTheme();
|
|
||||||
|
|
||||||
if (newTheme === systemTheme) {
|
|
||||||
localStorage.removeItem('theme');
|
|
||||||
} else {
|
|
||||||
localStorage.setItem('theme', newTheme);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加防抖
|
|
||||||
if (transitionTimeout) {
|
|
||||||
clearTimeout(transitionTimeout);
|
|
||||||
}
|
|
||||||
|
|
||||||
transitionTimeout = setTimeout(() => {
|
|
||||||
transitioning = false;
|
|
||||||
}, 300);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 监听系统主题变化
|
|
||||||
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
|
||||||
|
|
||||||
const handleMediaChange = (e: MediaQueryListEvent): void => {
|
|
||||||
if (!localStorage.getItem('theme')) {
|
|
||||||
const newTheme = e.matches ? 'dark' : 'light';
|
|
||||||
document.documentElement.dataset.theme = newTheme;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 添加系统主题变化监听
|
|
||||||
addListener(mediaQuery, 'change', handleMediaChange as EventListener);
|
|
||||||
|
|
||||||
// 为每个按钮添加事件
|
// 初始化主题
|
||||||
themeToggleButtons.forEach(button => {
|
const initializeTheme = (): void => {
|
||||||
(button as HTMLElement).style.pointerEvents = 'auto';
|
const storedTheme = localStorage.getItem('theme');
|
||||||
|
const systemTheme = getSystemTheme();
|
||||||
// 创建点击处理函数
|
|
||||||
const clickHandler = (e: Event) => {
|
// 按照逻辑优先级应用主题
|
||||||
e.preventDefault();
|
if (storedTheme) {
|
||||||
e.stopPropagation();
|
document.documentElement.dataset.theme = storedTheme;
|
||||||
toggleTheme();
|
} else if (systemTheme) {
|
||||||
|
document.documentElement.dataset.theme = systemTheme;
|
||||||
|
} else {
|
||||||
|
document.documentElement.dataset.theme = 'light';
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 点击事件 - 使用捕获模式
|
// 切换主题
|
||||||
addListener(button, 'click', clickHandler, { capture: true });
|
const toggleTheme = (): void => {
|
||||||
|
if (transitioning) {
|
||||||
// 键盘事件
|
return;
|
||||||
addListener(button, 'keydown', ((e: KeyboardEvent) => {
|
|
||||||
if (e.key === 'Enter' || e.key === ' ') {
|
|
||||||
e.preventDefault();
|
|
||||||
toggleTheme();
|
|
||||||
}
|
}
|
||||||
}) as EventListener);
|
|
||||||
});
|
transitioning = true;
|
||||||
|
|
||||||
// 处理移动端主题切换容器
|
// 获取当前主题
|
||||||
const themeToggleContainer = document.getElementById('theme-toggle-container');
|
const currentTheme = document.documentElement.dataset.theme;
|
||||||
if (themeToggleContainer) {
|
const newTheme = currentTheme === 'light' ? 'dark' : 'light';
|
||||||
addListener(themeToggleContainer, 'click', (e: Event) => {
|
|
||||||
|
// 更新 HTML 属性
|
||||||
|
document.documentElement.dataset.theme = newTheme;
|
||||||
|
|
||||||
|
// 更新本地存储
|
||||||
|
const systemTheme = getSystemTheme();
|
||||||
|
|
||||||
|
if (newTheme === systemTheme) {
|
||||||
|
localStorage.removeItem('theme');
|
||||||
|
} else {
|
||||||
|
localStorage.setItem('theme', newTheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加防抖
|
||||||
|
if (transitionTimeout) {
|
||||||
|
clearTimeout(transitionTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
transitionTimeout = setTimeout(() => {
|
||||||
|
transitioning = false;
|
||||||
|
}, 300);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 监听系统主题变化
|
||||||
|
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||||
|
|
||||||
|
const handleMediaChange = (e: MediaQueryListEvent): void => {
|
||||||
|
if (!localStorage.getItem('theme')) {
|
||||||
|
const newTheme = e.matches ? 'dark' : 'light';
|
||||||
|
document.documentElement.dataset.theme = newTheme;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 添加系统主题变化监听
|
||||||
|
addListener(mediaQuery, 'change', handleMediaChange as EventListener);
|
||||||
|
|
||||||
|
// 为每个按钮添加事件
|
||||||
|
themeToggleButtons.forEach(button => {
|
||||||
|
(button as HTMLElement).style.pointerEvents = 'auto';
|
||||||
|
|
||||||
|
// 创建点击处理函数
|
||||||
|
const clickHandler = (e: Event) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
toggleTheme();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 点击事件 - 使用捕获模式
|
||||||
|
addListener(button, 'click', clickHandler, { capture: true, passive: false });
|
||||||
|
|
||||||
|
// 键盘事件
|
||||||
|
addListener(button, 'keydown', ((e: KeyboardEvent) => {
|
||||||
|
if (e.key === 'Enter' || e.key === ' ') {
|
||||||
|
e.preventDefault();
|
||||||
|
toggleTheme();
|
||||||
|
}
|
||||||
|
}) as EventListener);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 处理移动端主题切换容器
|
||||||
|
const themeToggleContainer = document.getElementById('theme-toggle-container');
|
||||||
|
if (themeToggleContainer) {
|
||||||
|
addListener(themeToggleContainer, 'click', (e: Event) => {
|
||||||
|
const target = e.target as HTMLElement;
|
||||||
|
if (target.id !== 'theme-toggle-button' && !target.closest('#theme-toggle-button')) {
|
||||||
|
e.stopPropagation();
|
||||||
|
toggleTheme();
|
||||||
|
}
|
||||||
|
}, { capture: true, passive: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加事件代理,确保即使在DOM重建后点击事件也能正常工作
|
||||||
|
addListener(document.body, 'click', (e: Event) => {
|
||||||
const target = e.target as HTMLElement;
|
const target = e.target as HTMLElement;
|
||||||
if (target.id !== 'theme-toggle-button' && !target.closest('#theme-toggle-button')) {
|
// 检查是否点击了主题切换按钮
|
||||||
|
if (target.id === 'theme-toggle-button' || target.closest('#theme-toggle-button')) {
|
||||||
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
toggleTheme();
|
toggleTheme();
|
||||||
}
|
}
|
||||||
});
|
}, { capture: true, passive: false });
|
||||||
}
|
|
||||||
|
// 初始化主题
|
||||||
// 初始化主题
|
initializeTheme();
|
||||||
initializeTheme();
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 注册清理函数
|
// 注册清理函数
|
||||||
@ -229,8 +245,14 @@ const {
|
|||||||
|
|
||||||
// 初始化函数
|
// 初始化函数
|
||||||
function init(): void {
|
function init(): void {
|
||||||
setupThemeToggle();
|
// 确保先清理之前的事件
|
||||||
registerCleanup();
|
cleanup();
|
||||||
|
|
||||||
|
// 设置延时确保DOM已完全更新
|
||||||
|
setTimeout(() => {
|
||||||
|
setupThemeToggle();
|
||||||
|
registerCleanup();
|
||||||
|
}, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 在页面加载后初始化
|
// 在页面加载后初始化
|
||||||
@ -245,6 +267,19 @@ const {
|
|||||||
document.addEventListener('astro:page-load', init);
|
document.addEventListener('astro:page-load', init);
|
||||||
|
|
||||||
// Swup页面内容替换后重新初始化
|
// Swup页面内容替换后重新初始化
|
||||||
document.addEventListener('swup:contentReplaced', init);
|
document.addEventListener('swup:contentReplaced', () => {
|
||||||
|
// 先清理现有事件
|
||||||
|
cleanup();
|
||||||
|
|
||||||
|
// 确保在微任务队列后执行,让DOM完全更新
|
||||||
|
Promise.resolve().then(() => {
|
||||||
|
// 再次确保事件已清理
|
||||||
|
cleanup();
|
||||||
|
// 重新设置主题切换
|
||||||
|
setupThemeToggle();
|
||||||
|
// 注册新的清理函数
|
||||||
|
registerCleanup();
|
||||||
|
});
|
||||||
|
});
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
Loading…
Reference in New Issue
Block a user