JavaScript 脚本与全局背景图

什么是 JavaScript?

JavaScript(简称 JS)是一种编程语言,用来让网页”动”起来。在博客中,JS 可以:

  • 动态修改页面内容
  • 响应用户操作(点击、滚动等)
  • 加载外部资源(图片、数据等)

本博客的背景图系统

设计目标

  1. 首页文章列表:每篇文章显示不同的封面图
  2. 上一篇/下一篇:显示随机背景图
  3. 相关推荐:显示随机背景图
  4. 文章页顶部:显示随机背景图

文件结构

1
2
3
4
5
6
7
8
source/
├── img/
│ └── covers/ # 43 张封面图
│ ├── 1.webp
│ ├── 2.webp
│ └── ...
└── js/
└── randomBg.js # 背景图控制脚本

JavaScript 基础语法

变量

1
2
3
4
5
6
7
// 声明变量
var name = "Henry"; // 字符串
var count = 43; // 数字
var isReady = true; // 布尔值

// 常量(不可修改)
const MAX_COUNT = 43;

函数

1
2
3
4
5
6
7
// 定义函数
function greet(name) {
return "Hello, " + name;
}

// 调用函数
greet("Henry"); // 返回 "Hello, Henry"

数组

1
2
3
4
5
6
7
8
9
10
// 创建数组
var fruits = ["apple", "banana", "orange"];

// 访问元素
fruits[0]; // "apple"

// 遍历数组
fruits.forEach(function(fruit) {
console.log(fruit);
});

条件判断

1
2
3
4
5
6
7
var age = 18;

if (age >= 18) {
console.log("成年");
} else {
console.log("未成年");
}

randomBg.js 脚本详解

完整代码

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
// randomBg.js - 本地图片版
(function() {
'use strict';

// ========== 配置 ==========
var COVER_COUNT = 43; // 封面图总数
var LOCAL_PATH = '/img/covers/'; // 图片路径
var EXT = '.webp'; // 图片扩展名

// ========== 哈希函数 ==========
// 根据字符串生成唯一的数字
function hash(str) {
var h = 0;
for (var i = 0; i < str.length; i++) {
h = ((h << 5) - h + str.charCodeAt(i)) | 0;
}
return Math.abs(h);
}

// 根据种子获取图片索引 (1-43)
function getIdx(seed) {
return (hash(seed) % COVER_COUNT) + 1;
}

// ========== 功能函数 ==========

// 1. 替换首页文章列表封面图
function replaceCoverImages() {
// 获取所有文章卡片
var posts = document.querySelectorAll('.recent-post-item');

posts.forEach(function(post) {
// 找到文章链接
var link = post.querySelector('a[href]');
if (!link) return;

// 根据链接生成图片索引
var idx = getIdx(link.href);
var imgUrl = LOCAL_PATH + idx + EXT;

// 替换图片
var img = post.querySelector('img');
if (img) {
img.src = imgUrl; // 设置图片地址
}
});
}

// 2. 替换上一篇/下一篇背景图
function replacePaginationBg() {
// 获取所有上一篇/下一篇和相关推荐
var items = document.querySelectorAll('.pagination-related, .relatedPosts .relatedPost');

items.forEach(function(item, index) {
// 找到链接,提取文章标识
var link = item.querySelector('a[href]');
var seed = 'default_' + index;

if (link) {
// 从 URL 中提取 /posts/ 后面的部分
var match = link.href.match(/\/posts\/([^\/]+)/);
if (match) {
seed = match[1]; // 使用文章的 abbrlink
}
}

// 设置背景图
var imgUrl = LOCAL_PATH + getIdx(seed) + EXT;
item.style.backgroundImage = 'url("' + imgUrl + '")';
item.style.backgroundSize = 'cover';
item.style.backgroundPosition = 'center';
});
}

// 3. 替换文章页顶部图
function replaceTopImg() {
var header = document.querySelector('#page-header');
if (!header) return;

// 使用当前页面路径作为种子
var imgUrl = LOCAL_PATH + getIdx(window.location.pathname) + EXT;
header.style.backgroundImage = 'url("' + imgUrl + '")';
}

// ========== 执行入口 ==========
function runAll() {
replaceCoverImages();
replacePaginationBg();
replaceTopImg();
}

// 页面加载完成后执行
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', runAll);
} else {
runAll();
}

// 页面完全加载后再次执行(确保动态内容)
window.addEventListener('load', setTimeout(runAll, 100));

// 支持 PJAX(无刷新加载)
document.addEventListener('pjax:complete', setTimeout(runAll, 100));
})();

代码逐行解释

1. 立即执行函数

1
2
3
4
(function() {
'use strict';
// ... 代码 ...
})();
  • (function() { ... })():定义并立即执行一个函数
  • 'use strict':启用严格模式,避免一些常见错误
  • 这种写法可以避免污染全局变量

2. 哈希函数

1
2
3
4
5
6
7
function hash(str) {
var h = 0;
for (var i = 0; i < str.length; i++) {
h = ((h << 5) - h + str.charCodeAt(i)) | 0;
}
return Math.abs(h);
}

作用:将字符串转换为唯一的数字

举例

  • hash("abc") 可能返回 12345
  • hash("def") 可能返回 67890
  • 相同的输入总是返回相同的输出

为什么需要:确保每篇文章根据其链接/标题生成固定的图片索引

3. 获取图片索引

1
2
3
function getIdx(seed) {
return (hash(seed) % COVER_COUNT) + 1;
}
  • hash(seed):将种子转换为数字
  • % COVER_COUNT:取余数,确保在 0-42 之间
  • + 1:调整为 1-43

4. DOM 查询

1
document.querySelectorAll('.recent-post-item')
  • document:整个网页文档
  • querySelectorAll():查找所有匹配的元素
  • .recent-post-item:CSS 类选择器

常用查询方法

方法说明
querySelector('.class')查找第一个匹配的元素
querySelectorAll('.class')查找所有匹配的元素
getElementById('id')通过 ID 查找元素

5. 修改元素属性

1
img.src = imgUrl;  // 修改图片地址
  • img.src:图片元素的 src 属性
  • 设置新值后,浏览器会加载新图片

6. 修改元素样式

1
2
item.style.backgroundImage = 'url("' + imgUrl + '")';
item.style.backgroundSize = 'cover';
  • element.style.属性名:修改元素的行内样式
  • backgroundImage:背景图片
  • backgroundSize: cover:背景图覆盖整个元素

脚本注入配置

_config.butterfly.yml 中添加:

1
2
3
4
5
inject:
head:
- <link rel="stylesheet" href="/css/custom.css">
bottom:
- <script src="/js/randomBg.js"></script>

说明

  • head:在 </head> 标签前插入(用于 CSS)
  • bottom:在 </body> 标签前插入(用于 JS)

调试技巧

1. 查看控制台日志

在浏览器中按 F12 → Console 面板,可以看到脚本输出的日志。

2. 手动执行函数

在控制台中输入:

1
2
3
replaceCoverImages();
replacePaginationBg();
replaceTopImg();

可以手动触发图片替换。

3. 检查元素

右键点击元素 → 检查,查看:

  • img 标签的 src 属性是否正确
  • 元素的 background-image 样式

4. 清除缓存

修改 JS 后需要清除浏览器缓存:

  • Ctrl + Shift + R(Windows/Linux)
  • Cmd + Shift + R(Mac)

常见问题

问题1:图片不显示

可能原因

  • 图片路径错误
  • 图片文件不存在

排查方法

  1. 在控制台检查 img.src 的值
  2. 在浏览器中直接访问图片 URL

问题2:所有文章显示相同图片

可能原因

  • 哈希函数返回相同值
  • 种子值不唯一

解决方法:确保种子值(seed)能区分不同文章

问题3:图片闪烁

可能原因

  • 主题先加载默认图片,JS 再替换

解决方法

  1. 使用本地图片(加载更快)
  2. 在 CSS 中设置占位背景色

参考资源