Repository navigation
Expand file tree
/
Copy pathscript.js
More file actions
179 lines (153 loc) · 5.55 KB
/
Copy pathscript.js
File metadata and controls
179 lines (153 loc) · 5.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/**
* 静态博客核心脚本 —— 加载 Markdown、渲染、路由
* 依赖 marked (CDN)
*/
(function() {
// ---------- 博客数据 ----------
// 手动维护文章列表:file 对应 blogs/ 下的文件名,title 显示在侧边栏
const posts = [
{ file: "blog_1.md", title: "📝 第一篇博客 · 示例" },
{ file: "template.md", title: "📋 博客写作模板 (供参考)" }
];
// ---------- DOM 元素 ----------
const postListEl = document.getElementById('post-list');
const contentEl = document.getElementById('content-display');
// 基础路径:所有文章存放在 blogs 文件夹下
const BLOG_FOLDER = 'blogs/';
// 标记是否正在加载 (避免重复请求)
let isLoading = false;
// ---------- 渲染文章列表 ----------
function renderSidebar() {
postListEl.innerHTML = ''; // 清空
posts.forEach(post => {
const li = document.createElement('li');
li.textContent = post.title;
li.dataset.file = post.file; // 存储文件名,用于事件
li.setAttribute('role', 'button');
li.setAttribute('tabindex', '0');
postListEl.appendChild(li);
});
}
// ---------- 高亮当前选中文章 ----------
function highlightCurrentPost(fileName) {
const allItems = document.querySelectorAll('#post-list li');
allItems.forEach(li => {
const file = li.dataset.file;
if (file === fileName) {
li.classList.add('active');
li.setAttribute('aria-current', 'true');
} else {
li.classList.remove('active');
li.removeAttribute('aria-current');
}
});
}
// ---------- 显示内容区的加载状态 ----------
function showLoading() {
contentEl.innerHTML = '<div class="loading">📖 加载文章中…</div>';
}
// ---------- 显示错误信息 ----------
function showError(message) {
contentEl.innerHTML = `<div class="error-message">❌ ${message}</div>`;
}
// ---------- 根据文件名加载并渲染 markdown ----------
async function loadPostByFile(fileName) {
if (!fileName || isLoading) return;
// 高亮侧边栏
highlightCurrentPost(fileName);
// 显示加载占位
showLoading();
isLoading = true;
const filePath = `${BLOG_FOLDER}${fileName}`;
try {
const response = await fetch(filePath);
if (!response.ok) {
throw new Error(`无法读取文件 (HTTP ${response.status})`);
}
const markdownText = await response.text();
// 使用 marked 解析为 HTML
const htmlContent = marked.parse(markdownText, {
gfm: true, // GitHub 风格 Markdown
breaks: true, // 换行转换为 <br>
headerIds: true, // 为标题生成 id
mangle: false // 避免邮箱地址转义
});
// 渲染到内容区域
contentEl.innerHTML = htmlContent;
// 可选:滚动内容区到顶部
contentEl.scrollTop = 0;
} catch (error) {
console.error('Fetch error:', error);
showError(`加载失败:${error.message}。请检查文件是否存在于 ${filePath} ,并通过本地服务器访问。`);
} finally {
isLoading = false;
}
}
// ---------- 从 URL hash 获取文件名并加载 ----------
function loadPostFromHash() {
let hash = window.location.hash;
if (!hash || hash === '#') {
// 无 hash 则默认加载第一篇,并设置 hash (会触发再次调用)
if (posts.length > 0) {
window.location.hash = posts[0].file;
}
return;
}
// 去掉开头的 '#'
const fileName = hash.substring(1);
// 检查 posts 中是否有这个文件 (防止手动输入错误)
const matchedPost = posts.find(p => p.file === fileName);
if (matchedPost) {
loadPostByFile(fileName);
} else {
// 未知 hash: 显示提示,同时清除高亮
highlightCurrentPost(null);
contentEl.innerHTML = `<div class="error-message">🔍 未找到文章 "${fileName}",可点击左侧有效列表。</div>`;
}
}
// ---------- 事件监听 ----------
function setupEventListeners() {
// 侧边栏点击委托 (点击 li 或内层)
postListEl.addEventListener('click', (e) => {
const li = e.target.closest('li');
if (!li || !li.dataset.file) return;
const file = li.dataset.file;
// 改变 hash 即会触发 hashchange 进而加载文章
window.location.hash = file;
});
// 键盘辅助 (Enter 键)
postListEl.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
const li = e.target.closest('li');
if (li && li.dataset.file) {
e.preventDefault();
window.location.hash = li.dataset.file;
}
}
});
// hash 变化时加载相应文章
window.addEventListener('hashchange', loadPostFromHash);
}
// ---------- 初始化 ----------
function init() {
renderSidebar();
setupEventListeners();
// 如果初始没有 hash,设置为第一篇;否则直接加载
if (!window.location.hash || window.location.hash === '#') {
if (posts.length > 0) {
window.location.hash = posts[0].file;
} else {
contentEl.innerHTML = '<p>暂无博客文章,请先在 posts 数组添加。</p>';
}
} else {
// 有 hash 则手动触发一次加载 (但需要确保渲染列表后调用)
loadPostFromHash();
}
}
// 等待 DOM 完全加载后再执行
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init(); // DOM 已经就绪
}
})();