Bootstrap 3 的 Archives 模板里,月份列表以前点一下能展开、能收起,「全部展开/收缩」也好用。某次站点优化之后,所有月份全摊开、点击毫无反应。定案不是选择器坏了,也不是归档函数输出变了,而是内联脚本跑得比 jQuery 早。
Uncaught ReferenceError: jQuery is not defined 报在模板内联脚本那一行,IIFE 的实参 (jQuery, window) 是立即求值的,DOM-ready 保护救不了它。页面解析到这行时 jQuery 还没就绪,整段作废,所以连 $l.hide() 都没执行。
一、从页面表现就能推出的结论
模板开头是 $l.hide(); $l_f.show();,正常执行后只该展开第一个月。而实际截图里 11 月 4 篇、10 月 40 篇全部摊开——说明整段 JS 一行都没跑,不是「点击没绑上」这种局部故障,后面的 .on('click') 自然一个也没发生。
| 嫌疑 | 判定 |
|---|---|
| jQuery 被挪到页脚、或被加了 defer / async / 合并延迟 | 主因 站点此前发过《基于 Lighthouse 参数对 WordPress 网站优化提速》,JS 延迟加载正是这类优化的标准动作,时间线对得上 |
换成了 jQuery Slim,不含 effects,slideToggle 不是函数 |
次要可能 |
| 缓存 / 静态化插件剥掉了内联脚本 | 次要可能 |
| jQuery 3 太新,API 被移除 | 排除 $(function(){})、.data()、.slideToggle() 在 3.x 都还在 |
二、Console 两条报错,各管各的
| 报错 | 含义 |
|---|---|
Uncaught ReferenceError: jQuery is not definedarchives:211:4 |
就是折叠失效的根因。脚本写在 get_header(); ?> 正下方、DOM 之前,全靠 jQuery 已在 head 里加载完 |
Uncaught TypeError: e.indexOf is not a functionjquery.min.js?v=3.7.1.2:2:8064 |
jQuery 3 删掉了事件版 .load(fn),老代码把函数传进去就对函数取 .indexOf。可能波及左侧 tab 或别的老插件,与折叠无关,别混在一起改 |
jQuery、.fn.slideToggle、#archive-list、.al_mon、#al_expand_collapse 全都能取到,最后一行 length 返回 1。元素在、库也在,唯一的差别是时间。
三、不需要提供任何 PHP 文件
截图已经证明 HTML 结构完整(月份分组、「全部展开/收缩」都在),所以 zww_archives_list() 那一层基本可以排除。真要取证,按这个优先级,别一次全发:
- 页面 Ctrl+U 里所有 script 行 + Elements 中
#archive-list的 HTML:反映插件与缓存改写后的实际输出,比 PHP 源码更准 - header.php / footer.php:看
wp_head()/wp_footer()在不在、jQuery 在 head 还是 footer、有没有 defer / async - functions.php,或搜
zww_archives_list找到的文件:只有控制台显示 jQuery 在、但.al_mon数量为 0 时才用得上
顺带看左边「关于我 / 网站归档」几个 tab 能不能切:能切说明 bootstrap.js 在,只是 jQuery 这层挂了;连 tab 都切不动,更坐实加载顺序问题。
四、第一版修复:等 DOM,再等 jQuery
把模板里原来的 (function ($, window) { ... })(jQuery, window); 换成「先等 DOM、再取 window.jQuery、没到就每 50ms 重试、最多 5 秒」的外层封装,内部 .al_mon / slideToggle / 展开收缩逻辑一个字不改:
(function () {
function ready(fn) {
if (document.readyState !== 'loading') { fn(); }
else { document.addEventListener('DOMContentLoaded', fn); }
}
function boot() {
var $ = window.jQuery;
if (!$ || !$.fn || !$.fn.slideToggle) { return false; }
// 原逻辑原样放在这里
return true;
}
ready(function () {
if (!boot()) {
var n = 0, t = setInterval(function () {
if (boot() || ++n > 100) { clearInterval(t); }
}, 50);
}
});
})();
这一版把「jQuery slim 没有 slideToggle」也一并挡了。但代价是把 60 行塞进模板,既难维护,又引出了下面这个新问题。
五、换上之后,编辑器代码高亮整页失效
| 可能 | 要不要紧 |
|---|---|
那行中文全角注释 // DOM 好了再找元素;…(最多 5 秒) 让词法器停摆 |
只影响编辑器显示,浏览器执行时注释内容无关紧要 |
粘贴破坏了 get_header(); ?> 或结尾 <?php get_footer(); ?> 的标签边界 |
真问题 这个才会让前台 500 / 白屏 |
先做两个只读判断,别急着再改:另开隐身窗口刷 /archives(避开缓存),页面能开、报错消失、点月份能收缩,就说明代码其实已经生效,高亮纯粹是花屏;再 F5 刷一下编辑器页面,有时只是没重绘。
六、两条收口路线
不重粘整段,只把那一行中文注释换成英文,其余一个字不动,前台行为完全不变:
// Wait for DOM, then wait for jQuery if it is deferred or async.
把模板里整个 script 块删掉,模板回到纯 PHP + HTML,编辑器高亮必然立刻回来;JS 挪到 functions.php,挂到 jquery 句柄之后。用 nowdoc 防止 PHP 把 JS 里的 $ 当变量解析,保留轮询作为 defer / async 的双保险:
add_action( 'wp_enqueue_scripts', function () {
// 模板文件名不确定就删掉这三行,全站输出也没害处
if ( ! is_page_template( 'archives.php' ) ) {
return;
}
wp_enqueue_script( 'jquery' );
$js = <<<'JS'
(function () {
function init() {
var $ = window.jQuery;
if (!$ || !$.fn || !$.fn.slideToggle) return false;
$(function () {
var $a = $('#archive-list'),
$m = $('.al_mon', $a),
$l = $('.al_post_list', $a),
$lf = $('.al_post_list:first', $a);
$l.hide();
$lf.show();
$m.css('cursor', 's-resize').on('click', function () {
$(this).next().slideToggle(400);
});
function animate(i, status, s) {
if (i > $l.length) return;
$l.eq(i)[status === 'up' ? 'slideUp' : 'slideDown'](s, function () {
animate(i + 1, status, s - 10 < 1 ? 0 : s - 10);
});
}
$('#al_expand_collapse').on('click', function (e) {
e.preventDefault();
if ($(this).data('s')) {
$(this).data('s', '');
animate(0, 'up', 100);
} else {
$(this).data('s', 1);
animate(0, 'down', 100);
}
});
});
return true;
}
if (!init()) {
var n = 0, t = setInterval(function () {
if (init() || ++n > 100) clearInterval(t);
}, 50);
}
})();
JS;
wp_add_inline_script( 'jquery', $js, 'after' );
} );
追加到 functions.php 末尾即可,不要再写一个 <?php 开标签。
七、验收三条与预防
- 编辑器高亮恢复
- 前台
/archives默认只展开第一个月,点月份标题、点「全部展开/收缩」都能动 - Console 里不再有
jQuery is not defined
wp_add_inline_script('jquery', ..., 'after') 挂在 jquery 之后、删掉模板里的裸脚本。这样连轮询都不需要。
e.indexOf is not a function 跟折叠无关。真要清它,展开调用栈看下一帧是哪个脚本,或搜 \.load\( 把 $(window).load(function(){}) 改成 $(window).on('load', function(){});懒得查就装 Enable jQuery Migrate Helper 临时兜住。