Files
blog/themes/Ying/assets/js/modules/mypjax.js
T
zqlit 72bbf9db3e fix(pjax): 样式表版本同步修复部署后旧会话裸奔 + 关于页 CSS 冗余清理
- pjax:success 时比对新取 HTML 的样式表指纹,不一致则整页 reload 一次
  (sessionStorage 防循环)—— 根治「部署换了 CSS 指纹后,PJAX 旧会话
  永远跑旧样式」的线上 bug(2026-10-07 关于页裸奔实锤)
- cacheBust: true —— PJAX 请求加时间戳,绕开 HTML 长缓存
- main.css:删整段重复的响应式块/死选择器 .uk-scrollspy-inview .ab-bar/
  死代码 .about-card;.ab-stat 悬停归位进布局区
2026-10-07 22:54:28 +08:00

251 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 定义pjax 预设pjax
(function() {
if (typeof Pjax === 'undefined') {
console.error('Pjax library is not loaded.');
return;
}
var pjax = new Pjax({
// 这里填写页面中改变的部分(和 CSS 选择器用法一样)
selectors: ["title", "#pjax-container"],
// ★ cacheBust: PJAX 取新页面时在 URL 上加时间戳参数,绕开浏览器/CDN
// 对 HTML 的长期缓存。没有它,HTML 缓存期内 PJAX 会把「部署前的旧页面」
// 直接换进来(站点 HTML 的 Cache-Control 长达数天,踩过实锤)。
cacheBust: true,
elements: "a", // 拦截所有链接
switches: {
"#pjax-container": Pjax.switches.default
}
});
// 将 pjax 实例挂载到 window 以便调试或外部访问
window.pjax = pjax;
console.log("Pjax initialized:", pjax);
})();
// ── 静态资源版本同步(修复「部署后 PJAX 页面样式裸奔」)──
// ─────────────────────────────────────────────────────────────
// 根因:Pjax 只换 title 和 #pjax-container,<head> 里的指纹 CSS
// (style.min.<hash>.css)一旦因部署换了指纹,**旧会话**里所有后续
// PJAX 页面都跑在旧样式上 —— 新 HTML 的类(.ab-* 等)在旧 CSS 里
// 不存在 → 内容裸奔成纯文本(2026-10-07 关于页上线实锤)。
//
// 解法:pjax:success 时事件对象上带有本次请求(pjax 库把 options 的
// 键直接拷到 event 上,options.request = XMLHttpRequest),从
// responseText 解析出新页面的样式表指纹,与当前 document 比对;
// 不一致 → 整页 reload 一次拿全新资源(sessionStorage 保证每次会话
// 至多一次,防异常情况下的循环刷新)。
document.addEventListener("pjax:success", function (e) {
var request = e.request;
if (!request || typeof request.responseText !== "string" || !request.responseText) return;
var fetchedLinks, currentLinks, i, changed = false;
try {
var doc = new DOMParser().parseFromString(request.responseText, "text/html");
fetchedLinks = doc.querySelectorAll('link[rel="stylesheet"]');
currentLinks = document.querySelectorAll('link[rel="stylesheet"]');
} catch (err) { return; }
if (fetchedLinks.length !== currentLinks.length) {
changed = true;
} else {
for (i = 0; i < fetchedLinks.length; i++) {
if (fetchedLinks[i].getAttribute("href") !== currentLinks[i].getAttribute("href")) {
changed = true;
break;
}
}
}
if (!changed) return;
try {
if (sessionStorage.getItem("pjaxVerReload")) return; // 本会话已刷过
sessionStorage.setItem("pjaxVerReload", "1");
} catch (err) { /* 隐私模式等拿不到 sessionStorage:直接刷,概率极低 */ }
console.info("[pjax] 检测到样式表版本变化,整页刷新以加载新资源");
window.location.reload();
});
// UIKit 重新扫描(PJAX 必需)
// ─────────────────────────────────────────────────────────────
// UIKit 只在页面加载时做一次「全量 boot」:它遍历 document.body,把带
// uk-* 属性的元素逐个 connect 成组件。PJAX 换入的 DOM 是我们用
// innerHTML 塞进去的,UIKit 的 MutationObserver 虽然监听了 childList,
// 但因为「换入前属性就已存在」,observer 拿不到 attribute 的新增事件,
// 加上 PJAX 的替换发生在 UIKit 首次 boot 之后,组件就永远绑不上。
//
// 症状:uk-scrollspy 的元素停在 visibility:hidden(动画类的初始态),
// 下折叠的内容永久不可见,滚动也不会出现。
//
// 解法:换页后手动调一次 UIkit.update()。它会从传入节点向上走到
// document.body,再对整棵子树递归 connect —— 正好补上这次 boot。
function reinitUIKit(root) {
if (typeof window.UIkit === 'undefined' || typeof window.UIkit.update !== 'function') return;
var target = root || document.getElementById('pjax-container') || document.body;
try {
window.UIkit.update(target);
} catch (e) {
console.warn('UIkit.update failed:', e);
}
}
// 暴露给页面脚本:动态插入带 uk-* 属性的内容后,调一次即可让 UIKit 生效
window.reinitUIKit = reinitUIKit;
// pjax预先加载代码
function pjax_reload() {
console.log("Pjax reload triggered");
// 给pjax容器加入css过渡动画
// var pjaxContainer = document.getElementById("pjax-container");
// if (pjaxContainer) pjaxContainer.classList.add("uk-animation-slide-bottom-small");
// 评论区过渡动画
var selectors = [
".comment-meta",
".comment-author vcard",
".comment-metadata",
".comment-content",
".reply"
];
selectors.forEach(function(selector) {
document.querySelectorAll(selector).forEach(function(el) {
el.setAttribute("uk-scrollspy", "cls: uk-animation-fade; delay: 100");
});
});
// 说说页面过渡动画
var shuoshuoBody = document.getElementById("shuoshuo_body");
if (shuoshuoBody) {
shuoshuoBody.setAttribute("uk-scrollspy", "target: > .shuoshuo; cls: uk-animation-fade; delay: 250");
}
}
function refreshHeatmap() {
if (typeof renderHeatmap === 'function') {
try {
renderHeatmap();
} catch (e) {
console.error('renderHeatmap error:', e);
}
}
}
// pjax发送时期
document.addEventListener("pjax:send", function () {
// pjax动画减少圆圈动画
NProgress.configure({
showSpinner: false
});
// pjax执行动画开始
NProgress.start(); //加载动画效果开始
});
// Pjax 成功后,重新加载下面的函数
document.addEventListener("pjax:success", function () {
// 重载图片懒加载插件的js
if (typeof lazyLoadInit === 'function') {
lazyLoadInit({
// coverColor: "white",
// coverDiv: "<div style='display: flex; justify-content: center; align-items: center; height: 200px; color: #000000;'><span>优世界·一个记录生活点滴的小站</span></div>",
offsetBottom: 50,
offsetTopm: 50,
showTime: 1100,
queueInterval: 100
});
}
// 激活文章图片灯箱
if (window.ViewImage) {
ViewImage.init('.post-content img');
}
if (typeof window.refreshFloatingTools === 'function') {
window.refreshFloatingTools();
}
});
// pjax加载完成
document.addEventListener("pjax:complete", function () {
// 立即执行:进度条、基础 UI
if (typeof NProgress !== 'undefined') NProgress.done();
pjax_reload();
// ★ 关键:pjax_reload() 里刚给评论/说说等元素新加了 uk-scrollspy 属性,
// 此刻连同换入的整棵新 DOM 一起交给 UIKit 重新 boot一遍。
// 必须在 pjax_reload() 之后调用,否则新加的属性会被漏掉。
// (关于页的分节动画也走 UIKit scrollspy,所以同样靠这一步复活。)
reinitUIKit();
// 动态加载页面特定JS(PJAX适配)
// 检测当前页面是否为文章详情页,如果是则加载page-only.js
var currentPath = window.location.pathname;
var isArticlePage = document.querySelector('#Comments') !== null ||
document.querySelector('.post-content') !== null;
if (isArticlePage && !window._pageOnlyLoaded && window._pageOnlyScriptUrl) {
// 动态加载page-only.js(使用Hugo fingerprint后的正确URL)
var script = document.createElement('script');
script.src = window._pageOnlyScriptUrl;
script.onload = function() {
window._pageOnlyLoaded = true;
console.log('page-only.js loaded for PJAX navigation');
// 初始化Artalk等
if (typeof window.initArtalk === 'function') {
try { window.initArtalk(); } catch(e) { console.error('initArtalk error:', e); }
}
if (typeof window.initParagraphComments === 'function') {
try { window.initParagraphComments(); } catch(e) {}
}
};
document.body.appendChild(script);
}
// 下一帧执行:轻量初始化
requestAnimationFrame(function() {
if (typeof initCodeCopy === 'function') initCodeCopy();
if (typeof window.initSearch === 'function') {
try { window.initSearch(); } catch(e) {}
}
if (typeof window.refreshFloatingTools === 'function') {
window.refreshFloatingTools();
} else if (typeof window.initFloatingTools === 'function') {
try { window.initFloatingTools(); } catch(e) {}
}
// 文章动画
if (typeof window.initPostScrollspy === 'function') window.initPostScrollspy();
});
// 空闲时执行:非关键功能
var idle = window.requestIdleCallback || function(fn) { return setTimeout(fn, 200); };
idle(function() {
if (typeof window.initParagraphComments === 'function') {
try { window.initParagraphComments(); } catch(e) {}
}
if (typeof window.initInfiniteScroll === 'function' && window.enableInfiniteScroll) {
window.initInfiniteScroll();
}
if (typeof window.initLinkStatus === 'function') {
try { window.initLinkStatus(); } catch(e) {}
}
if (typeof window.initArtalk === 'function') {
try { window.initArtalk(); } catch(e) {}
}
if (typeof window.bsz_fetch === 'function') {
try { window.bsz_fetch(); } catch(e) {}
}
if (typeof window.initImageFrameReveal === 'function') {
try { window.initImageFrameReveal(); } catch(e) {}
}
// 链接卡片和中英文间距(DOM 扫描,非关键)
if (typeof window.initLinkify === 'function') window.initLinkify();
if (typeof window.initPangu === 'function') window.initPangu();
});
// 延迟:代码折叠
setTimeout(function() {
if (typeof window.initCodeFold === 'function') window.initCodeFold();
}, 300);
});