WordPress 归档页点击不能收缩:一次 jQuery 加载顺序的排查与收口

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 defined
archives:211:4
就是折叠失效的根因。脚本写在 get_header(); ?> 正下方、DOM 之前,全靠 jQuery 已在 head 里加载完
Uncaught TypeError: e.indexOf is not a function
jquery.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() 那一层基本可以排除。真要取证,按这个优先级,别一次全发:

  1. 页面 Ctrl+U 里所有 script 行 + Elements 中 #archive-list 的 HTML:反映插件与缓存改写后的实际输出,比 PHP 源码更准
  2. header.php / footer.php:看 wp_head() / wp_footer() 在不在、jQuery 在 head 还是 footer、有没有 defer / async
  3. 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 行塞进模板,既难维护,又引出了下面这个新问题。

五、换上之后,编辑器代码高亮整页失效

先把两件事分开:编辑器高亮是 CodeMirror 自己的词法着色,不等于 PHP / JS 语法坏了,也不等于前台跑不起来。
可能 要不要紧
那行中文全角注释 // DOM 好了再找元素;…(最多 5 秒) 让词法器停摆 只影响编辑器显示,浏览器执行时注释内容无关紧要
粘贴破坏了 get_header(); ?> 或结尾 <?php get_footer(); ?> 的标签边界 真问题 这个才会让前台 500 / 白屏

先做两个只读判断,别急着再改:另开隐身窗口刷 /archives(避开缓存),页面能开、报错消失、点月份能收缩,就说明代码其实已经生效,高亮纯粹是花屏;再 F5 刷一下编辑器页面,有时只是没重绘。

六、两条收口路线

路线 A:最小改动,推荐先试

不重粘整段,只把那一行中文注释换成英文,其余一个字不动,前台行为完全不变:

    // Wait for DOM, then wait for jQuery if it is deferred or async.

路线 B:彻底解决,顺带根治加载顺序

把模板里整个 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
以后别再踩:做 JS 延迟 / 异步加载优化时,把内联 script 排除掉,或者干脆按 WordPress 正规做法用 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 临时兜住。

分享到:

用微信扫一扫,分享给朋友


发布日期:

所属分类: WordPress 主题 标签:   



下一篇:

没有了,已经是最新文章