本文整理 Hexo 博客 main.js 中用到的 JavaScript 语法,以及一些前端必备的 JS 基础知识。

main.js 中用到的语法

main.js 负责三件事:① 从隐藏的 HTML 中读取文章数据,② 动态生成左侧分类侧边栏,③ 点击文章后通过 fetch 加载内容到右侧。

DOMContentLoaded — 等页面加载完再执行

1
2
3
4
document.addEventListener('DOMContentLoaded', function() {
// 页面 HTML 加载完毕后执行这里的代码
// 此时所有 DOM 元素都已存在,可以安全操作
});

通俗理解:就像等饭菜全部上齐了再动筷子。如果页面还没加载完就去操作元素,会找不到元素导致报错。

querySelector / querySelectorAll — 查找页面元素

1
2
3
4
5
6
7
// 找一个元素(返回第一个匹配的)
const nav = document.getElementById('categoryNav'); // 按 id 找
const card = document.querySelector('.card-title'); // 按 CSS 选择器找

// 找多个元素(返回一个列表,类似数组)
const cards = document.querySelectorAll('.category-card');
cards.forEach(function(card) { ... }); // 遍历每一个

通俗理解getElementById 按身份证号找人(唯一),querySelector 按特征找人(返回第一个符合的),querySelectorAll 找所有符合特征的人。

innerHTML — 往元素里塞 HTML

1
2
nav.innerHTML = '<div class="category-group">...</div>';
// 把字符串当作 HTML 插入到元素内部

通俗理解:就像往一个空盒子(<nav>)里放东西。innerHTML 直接塞 HTML 代码进去,浏览器会自动解析渲染。

classList — 操作元素的 class

1
2
3
4
5
6
7
8
// 添加一个 class(就像给元素贴标签)
el.classList.add('active');

// 移除一个 class(撕掉标签)
el.classList.remove('active');

// 切换 class(有就删,没有就加)
el.classList.toggle('open');

通俗理解classList 就像管理元素的”身份标签”。add 贴标签,remove 撕标签,toggle 开关(按一下开,再按一下关)。

forEach — 遍历数组

1
2
3
4
5
6
const fruits = ['苹果', '香蕉', '橘子'];

fruits.forEach(function(item, index) {
console.log('第' + index + '个: ' + item);
});
// 输出: 第0个: 苹果, 第1个: 香蕉, 第2个: 橘子

通俗理解:就像点名,把列表里的每个元素依次拿出来处理一遍。item 是当前元素,index 是序号(从 0 开始)。

对象 — 键值对存储

1
2
3
4
5
const categories = {};  // 空对象

categories['聊天项目'] = []; // 用分类名当"钥匙",数组当"值"
categories['聊天项目'].push({ title: 'TCP原理', url: '/xxx/' });
// 结果: { '聊天项目': [{ title: 'TCP原理', url: '/xxx/' }] }

通俗理解:对象就像一本字典——用”词”(key)查”释义”(value)。categories['聊天项目'] 就是用”聊天项目”这个词查到对应的文章列表。

fetch — 从服务器拿数据

1
2
3
4
5
6
7
8
9
10
11
12
fetch('/文章路径/')
.then(function(response) {
return response.text(); // 把响应转成文本
})
.then(function(html) {
// 拿到 HTML 字符串,可以显示在页面上
viewer.innerHTML = html;
})
.catch(function(err) {
// 如果网络出错,在这里处理
console.log('加载失败:', err);
});

通俗理解fetch 就像发快递请求——你给一个地址(URL),服务器把东西寄回来。.then() 是”收到快递后做什么”,.catch() 是”快递丢了怎么办”。这是异步操作,不会卡住页面。

DOMParser — 把 HTML 字符串变成可操作的对象

1
2
3
4
const parser = new DOMParser();
const doc = parser.parseFromString(html字符串, 'text/html');
const article = doc.querySelector('.post-detail');
// 现在可以像操作正常页面一样操作这个 HTML 字符串了

通俗理解fetch 拿回来的是一串”文本”,浏览器不认识。DOMParser 像一个翻译官,把这串文本翻译成浏览器能操作的 DOM 对象,然后你就可以用 querySelector 提取想要的部分。

closest — 向上查找祖先元素

1
2
3
// 用户点击了 <span> 里的文字
// closest 往上找,找到最近的 .category-group-header 元素
const header = e.target.closest('.category-group-header');

通俗理解:你点了一个很里面的小元素,但你想知道它属于哪个”父容器”。closest 就像顺着家谱往上查,找到第一个符合条件的”长辈”。

style.display — 控制元素显示/隐藏

1
2
element.style.display = 'none';   // 隐藏(不占位置)
element.style.display = 'block'; // 显示

通俗理解display: none 就像把东西收到抽屉里(完全看不见),display: block 就像拿出来摆在桌上。


JS 基础知识

变量声明 — var / let / const

1
2
3
var name = '小明';     // 旧式写法,有作用域问题(尽量少用)
let age = 18; // 可修改的变量(推荐)
const PI = 3.14; // 常量,声明后不能改(推荐默认用这个)
关键字 能改吗 作用域 推荐
var 函数级(容易出 bug) ❌ 少用
let 块级 {} ✅ 需要改时用
const 不能 块级 {} ✅ 默认用这个

通俗理解const 就像刻在石头上的字(不能改),let 就像写在白板上的字(可以擦掉重写)。能用 const 就用 const,代码更安全。

函数 — 把一段代码打包

1
2
3
4
5
6
7
// 定义函数(给一段代码起个名字)
function sayHello(name) {
return '你好, ' + name + '!';
}

// 调用函数
const msg = sayHello('小明'); // msg = '你好, 小明!'

通俗理解:函数就像一个”菜谱”——定义时只写步骤不执行,调用时才真正”做菜”。return 是”出锅”——把结果交出去。

事件 — 用户操作触发代码

1
2
3
4
5
// 点击事件
element.addEventListener('click', function(e) {
console.log('被点击了!');
console.log('点击的目标:', e.target); // e.target 是具体被点的元素
});

常见事件:

事件名 触发时机
click 鼠标点击
DOMContentLoaded HTML 加载完成
mouseover 鼠标移入
keydown 键盘按下

通俗理解addEventListener 就像在门口装了一个门铃——平时没反应,有人按(触发事件)时才响(执行函数)。

字符串拼接 — 拼文字

1
2
3
4
5
6
7
8
const name = '小明';
const age = 18;

// 方式1: + 号拼接(传统)
const msg1 = '我叫' + name + ',今年' + age + '岁';

// 方式2: 模板字符串(推荐,用反引号 ``)
const msg2 = `我叫${name},今年${age}岁`;

通俗理解:模板字符串(反引号 + ${})就像填空题,把变量直接嵌入到句子中,比用 + 号拼接更直观。

条件判断 — if / 三元运算符

1
2
3
4
5
6
7
8
9
10
11
// if 语句
if (score >= 60) {
console.log('及格');
} else {
console.log('不及格');
}

// 三元运算符(简写版 if-else)
const result = score >= 60 ? '及格' : '不及格';
const openClass = catIndex === 0 ? ' open' : '';
// ↑ 条件 ↑ 成立时 ↑ 不成立时

通俗理解:三元运算符是 if-else 的”一句话版本”。条件 ? 值1 : 值2 读作”条件成立给我值1,否则给我值2”。

for…in — 遍历对象的属性

1
2
3
4
5
6
const person = { name: '小明', age: 18, city: '北京' };

for (const key in person) {
console.log(key + ': ' + person[key]);
}
// 输出: name: 小明, age: 18, city: 北京

通俗理解for...in 遍历对象的”钥匙串”——拿到每一把钥匙(key),再用钥匙开锁取值(person[key])。


总结

main.js 的核心逻辑:

1
2
3
4
5
6
页面加载完成
→ 从隐藏的 HTML 中读取分类和文章数据(querySelector + forEach)
→ 用数据拼接 HTML 字符串,塞进侧边栏(innerHTML + 字符串拼接)
→ 给分类标题绑定点击事件(addEventListener + closest + classList.toggle)
→ 给文章项绑定点击事件(addEventListener + closest + fetch)
→ 点击文章时,fetch 拿到 HTML,用 DOMParser 解析,显示在右侧