newBlog / app /static /js /main.js
mistpe's picture
Upload 2 files
d82405d verified
// 全局工具函数和初始化
document.addEventListener('DOMContentLoaded', function() {
// 等待 marked 库加载完成
setTimeout(() => {
// 初始化搜索功能
initializeSearch();
// 初始化Markdown渲染
initializeMarkdownRendering();
// 初始化返回顶部按钮
initializeScrollToTop();
// 初始化移动端导航菜单
initializeMobileNavigation();
}, 100);
});
// 搜索功能实现
function initializeSearch() {
const searchInput = document.querySelector('.search-input');
const searchResults = document.querySelector('.search-results');
if (searchInput) {
searchInput.addEventListener('input', debounce(async (event) => {
const searchTerm = event.target.value.trim().toLowerCase();
if (searchTerm.length < 2) {
searchResults.style.display = 'none';
return;
}
try {
const response = await fetch(`/api/search?q=${encodeURIComponent(searchTerm)}`);
const data = await response.json();
if (data.articles.length > 0) {
displaySearchResults(data.articles, searchResults);
} else {
searchResults.innerHTML = '<div class="no-results">没有找到相关文章</div>';
}
searchResults.style.display = 'block';
} catch (error) {
console.error('搜索出错:', error);
searchResults.innerHTML = '<div class="error">搜索服务暂时不可用</div>';
}
}, 300));
// 点击其他区域关闭搜索结果
document.addEventListener('click', (event) => {
if (!event.target.closest('.search-container')) {
searchResults.style.display = 'none';
}
});
}
}
// Markdown渲染初始化
function initializeMarkdownRendering() {
if (typeof marked === 'undefined') {
console.error('Marked library not loaded');
return;
}
// 配置 marked
marked.use({
breaks: true,
gfm: true
});
const markdownElements = document.querySelectorAll('.markdown-body');
markdownElements.forEach(element => {
// 检查元素是否已经被渲染过
if (element.dataset.rendered) {
return;
}
try {
// 标记为已渲染
element.dataset.rendered = 'true';
// 如果内容是通过 Flask 的 markdown 过滤器渲染的,就不需要再次渲染
if (!element.classList.contains('server-rendered')) {
const content = element.textContent;
element.innerHTML = marked(content);
}
// 处理代码块
element.querySelectorAll('pre code').forEach(block => {
if (typeof hljs !== 'undefined') {
hljs.highlightElement(block);
}
});
// 为图片添加点击放大功能
element.querySelectorAll('img').forEach(img => {
img.addEventListener('click', () => {
openImageViewer(img.src);
});
});
} catch (error) {
console.error('Markdown渲染错误:', error);
}
});
}
// 返回顶部按钮实现
function initializeScrollToTop() {
const scrollTopButton = document.createElement('button');
scrollTopButton.className = 'scroll-top-button';
scrollTopButton.innerHTML = '↑';
document.body.appendChild(scrollTopButton);
window.addEventListener('scroll', debounce(() => {
if (window.scrollY > 500) {
scrollTopButton.classList.add('visible');
} else {
scrollTopButton.classList.remove('visible');
}
}, 100));
scrollTopButton.addEventListener('click', () => {
window.scrollTo({
top: 0,
behavior: 'smooth'
});
});
}
// 移动端导航菜单实现
function initializeMobileNavigation() {
const menuButton = document.querySelector('.menu-button');
const navLinks = document.querySelector('.nav-links');
if (menuButton && navLinks) {
menuButton.addEventListener('click', () => {
navLinks.classList.toggle('active');
menuButton.classList.toggle('active');
});
// 点击导航链接后关闭菜单
navLinks.querySelectorAll('a').forEach(link => {
link.addEventListener('click', () => {
navLinks.classList.remove('active');
menuButton.classList.remove('active');
});
});
}
}
// 图片查看器实现
function openImageViewer(imageSrc) {
const viewer = document.createElement('div');
viewer.className = 'image-viewer';
viewer.innerHTML = `
<div class="image-viewer-content">
<img src="${imageSrc}" alt="预览图片">
<button class="close-button">×</button>
</div>
`;
viewer.addEventListener('click', (event) => {
if (event.target === viewer || event.target.className === 'close-button') {
viewer.remove();
}
});
document.body.appendChild(viewer);
}
// 工具函数: 防抖
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// 工具函数: 搜索结果显示
function displaySearchResults(articles, container) {
container.innerHTML = articles.map(article => `
<a href="/article/${article.slug}" class="search-result-item">
<h3>${highlightSearchTerm(article.title)}</h3>
${article.summary ? `<p>${highlightSearchTerm(article.summary)}</p>` : ''}
<span class="article-date">${formatDate(article.created_at)}</span>
</a>
`).join('');
}
// 工具函数: 高亮搜索词
function highlightSearchTerm(text, searchTerm) {
if (!searchTerm) return text;
const regex = new RegExp(searchTerm, 'gi');
return text.replace(regex, match => `<mark>${match}</mark>`);
}
// 工具函数: 日期格式化
function formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleDateString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit'
});
}
// 错误处理函数
function handleError(error, container) {
console.error('发生错误:', error);
container.innerHTML = `
<div class="error-message">
<p>抱歉,发生了一些错误</p>
<button onclick="window.location.reload()">刷新页面</button>
</div>
`;
}