WP Editor.md 导致 Document 主题首页「文章导航」消失的完整排查与修复记录

WP Editor.md 导致 Document 主题首页「文章导航」消失的完整排查与修复记录

环境:WordPress + WP Editor.md(wp-editormd)+ Document 主题(theme-document)
现象:启用 WP Editor.md 后,博客首页左侧「🗂️ 文章导航」整块不显示或「像消失了一样」;禁用插件后恢复正常。
结论:这不是 Markdown 标题解析失败,而是 前台脚本加载策略 + jQuery 句柄冲突 + 主题侧栏默认透明 叠加导致的兼容性问题。


一、现象描述

1.1 用户看到什么

  • 首页本应出现左侧 文章导航 栏,列出当前页各篇文章标题,点击可锚点跳转到对应 h2
  • 安装并启用 WP Editor.md,且通常开启了「支持前台评论 Markdown」等选项后,导航栏消失。
  • 浏览器控制台常见报错(修复前典型堆栈,行号随版本可能变化):
Uncaught ReferenceError: $ is not defined
    at monitor.js:11:1
    at main.js:2:1
    at index.js:4:1

Uncaught ReferenceError: jQuery is not defined

Uncaught ReferenceError: Prism is not defined

1.2 容易产生的误解

误解 实际情况
「Markdown 标题不是 HTML,主题解析不到」 首页导航来自 $wp_query 文章列表 + post_title,不解析正文 Markdown
「文章总标题没写进目录所以整块没了」 单篇文章页才涉及总标题目录;首页是 列表导航,逻辑不同
「插件 CSS 把侧栏 display:none 了」 更常见是 opacity: 0 且 JS 未执行,DOM 仍在,肉眼看不见

二、Document 主题首页导航是怎么工作的

要理解 bug,必须先弄清主题 期望 的行为。

2.1 显示条件(PHP 层)

首页侧栏模板:template/index/sidebar-index-left.php

if ( nicen_theme_showArticleCate() ) {
    $catelog = nicen_theme_navigator();
    if ( ! empty( $catelog ) ) {
        // 输出 #space > #navigator ...
    }
}

两层门闩:

  1. nicen_theme_showArticleCate()
    • 博客首页 / 「最新文章」型首页:读取主题选项 document_show_left_nav
    • 分类、标签、搜索等:读取 document_show_else_left_nav
  2. nicen_theme_navigator() 返回值非空
    • 若当前查询结果里没有任何可展示文章,返回空字符串 → 整块 HTML 不输出(不是隐藏,是根本没有 DOM)

2.2 目录 HTML 如何生成(首页分支)

include/functions/common.phpnicen_theme_navigator()非单篇文章页(含首页)时:

global $wp_query;
foreach ( $wp_query->posts as $post ) {
    // 过滤「不在首页显示」的分类/标签
    // 生成 <li><a href="#h2{post_id}">1. 文章标题</a></li>
}

要点:

  • 数据来自 主查询 $wp_query->posts,与文章摘要、Markdown 正文无关。
  • 锚点 #h2{ID} 对应列表模板里每篇文章的 <h2 id="h2...">(见 template/index/article-list.php)。
  • 单篇文章页(与首页 bug 无关,但易混淆):目录首项为 文章总标题#document-post-title),其下为正文 ## / ### / ####(HTML 模式)或 h1 / h2 / h3 短代码(meta 模式);需主题选项 HTML 标签 目录模式以配合 WP Editor.md。

2.3 显示条件(CSS + JS 层)

即使 PHP 输出了 #space#navigator,用户仍可能「看不见」,因为主题还有一套 前端定位与显隐 机制。

CSS 默认透明

#navigator 在样式中默认:

html body .main-container #space #navigator {
  opacity: 0;
  position: fixed;
  /* ... */
}

首页导航容器还带 class isIndexsidebar-index-left.php)。

设计意图:避免侧栏在 JS 计算 left/top 之前 闪一下错位,等 monitor.js 算好位置后再 show()

JS 负责定位与显示

common/inline/monitor.js(在开启首页左侧导航时由 include/themes/load.php 引入):

let navigator = $('#navigator');
if (navigator.length > 0) {
    navigator.css('left', pos.left);
    navigator.css('top', space.getTop());
    navigator.show();  // 主要改 display;opacity 靠 inline style 或后续逻辑
}

common/main.js 还负责滚动时固定侧栏等行为,全部依赖 jQuery 全局 $

主题加载顺序(修复前的问题版本)曾存在:

  • 脚本 未声明 依赖 jquery
  • 头部 执行,早于 WP Editor.md 替换 jQuery 的时机

→ 一旦 $ 不存在,整链 JS 中断opacity: 0 永远得不到纠正。


三、WP Editor.md 做了什么(冲突从哪来)

3.1 插件前台模块的触发条件

wp-editormd/src/Main.php 在以下情况实例化前台控制器 EditormdFront\Controller

Config::get_option("support_front", "editor_basics") == "on"
|| Config::get_option("support_other_text", "editor_basics") !== ""
    ? new ControllerFront() : null;

即:开启 前台评论 Markdown,或填写了 自定义前台编辑器元素 ID,就会挂接 wp_enqueue_scripts

修复前的行为:只要满足上述配置,全站所有页面(含首页、归档页)都会执行 enqueue_front_scripts(),而首页根本没有 #comment 评论框——Editor.md 的配置脚本会在 document.ready 里发现没有目标 textarea 然后 return,但 jQuery 已被替换/注销

3.2 jQuery「兼容模式」与句柄替换

Front\Controller::enqueue_front_scripts()(修复前核心逻辑):

if (Config::get_option("jquery_compatible", "editor_advanced") !== "off") {
    wp_enqueue_script("jquery", null, null, array(), false);  // 头部加载
} else {
    wp_deregister_script("jquery");  // 注销 WordPress 默认 jquery
    wp_enqueue_script("jQuery-CDN", .../jquery.min.js, ..., true);  // 页脚、新句柄
}
wp_enqueue_script("Editormd_Front", ..., array("jquery" 或 "jQuery-CDN"));
wp_enqueue_script("Config_Front", ..., array("Editormd_Front"));
模式 行为 对主题的影响
兼容模式 开启jquery_compatibleoff 仍用核心 jquery,但可能强制 头部 加载 与主题 页脚 脚本顺序易错位
兼容模式 关闭 wp_deregister_script('jquery'),改用句柄 jQuery-CDN 主题若仍依赖句柄 jquery 或未声明依赖 → $ 未定义

同一插件内 KaTeX、MindMap、FrontStyle 等模块在修复前也会在 wp_enqueue_scripts 全站 重复执行类似的 jQuery 注销/注册逻辑,首页无辜中招。

3.3 Prism 报错(次要但会加剧排查难度)

开启语法高亮自动加载时,插件在 wp_print_footer_scripts 输出:

Prism.plugins.autoloader.languages_path = "...";

若主题或插件脚本顺序导致 Prism 全局变量尚未定义,会再抛 Prism is not defined。该错误与导航无直接关系,但说明 首页加载了本不需要的 Editor.md 前台资源


四、故障链:从插件启用到导航「消失」

下面是一条完整的因果链(修复前)。

flowchart TB
    A([启用前台评论 Markdown]) --> B[全站加载 Front / Config_Front]

    B --> C{jquery_compatible?}
    C -->|关闭| D[wp_deregister jquery<br/>改用 jQuery-CDN]
    C -->|开启| E[jquery 在 head 加载<br/>主题脚本在 footer]

    D --> F[main · monitor · index<br/>执行时 $ 未定义]
    E --> F
    F --> G[monitor.js 解析期报错<br/>$.fn.scrollUnique]
    G --> H[侧栏定位 / show 未执行]
    H --> I["navigator opacity 仍为 0"]
    I --> J([用户感知:导航消失])

    B --> K[首页无评论框<br/>Editor 逻辑空 return]
    K --> L([jQuery 被改动<br/>却无编辑器收益])

    classDef entry fill:#e3f2fd,stroke:#1565c0,color:#0d47a1
    classDef fault fill:#ffebee,stroke:#c62828,color:#b71c1c
    classDef side fill:#fff8e1,stroke:#f9a825,color:#f57f17
    class A entry
    class J fault
    class L side

4.1 为何「禁用插件就好」

禁用 WP Editor.md 后:

  • 不再 wp_deregister_script('jquery')
  • 不再全站加载 Config_Front / Prism 内联
  • WordPress 默认 jquery + 主题脚本依赖恢复正常 → monitor.js 跑通 → 侧栏可见

这证明 数据层(PHP 目录)往往是好的,问题在 资源加载与前端执行环境

4.2 为何「HTML 里可能有,但看不见」

用开发者工具检查 DOM,常见:

  • 存在 <div id="space"><aside id="navigator" class="... isIndex">
  • <ul> 内有 <li> 链接列表
  • 计算样式 opacity: 0visibility/display 导致不可见

所以这不是「没生成导航」,而是 生成后未被正确展示

4.3 与 Markdown 正文的关系(单篇页补充)

单篇文章页「整块不显示」曾有 两类独立原因

类型 原因 处理
目录为空 仅扫描正文标题、无 ## 且未包含总标题 目录首项固定为 post_titlenicen_theme_build_post_title_catalog_item
标题对不上 默认 meta 模式找

,Editor.md 存的是 <h2>
主题迁移为 HTML 标签 模式(document_catelog_mode = html
点击无效 正文 <h2>id nicen_theme_fit_html_cat_modethe_content 注入 id='h21'

当前单篇页:总标题 + 正文章节 均会进入侧栏(nicen_theme_build_body_catalog_items)。本文重点仍是 首页列表导航 + Editor.md 全站脚本


五、WordPress 脚本加载机制(原理速览)

理解修复方案需要一点 WP 脚本 API 背景。

5.1 wp_enqueue_script 与依赖

wp_enqueue_script( 'handle', $src, $deps, $ver, $in_footer );
  • $deps:必须先加载的句柄数组(如 array('jquery'))。
  • $in_footertrue 时脚本在 </body> 前输出;false 时在 <head> 输出。
  • 多个插件/主题在 wp_enqueue_scripts 钩子上注册,默认优先级 10;后注册的可声明依赖先注册的句柄。

5.2 wp_deregister_script 的风险

wp_deregister_script('jquery') 会移除核心注册的 jquery 句柄。之后:

  • 仅声明依赖 jquery 的脚本可能 拿不到 jQuery
  • 必须使用新句柄(如 jQuery-CDN)并在 同一请求内 由某处 wp_enqueue_script('jQuery-CDN')

若主题在优先级 10 注册、插件在 10 注销、主题在 999 才补救,则 中间任何在 10~999 之间执行且用到 $ 的内联或脚本都会炸

5.3 为何「优先级 100 / 999」能缓解

主题将 nicen_theme_load_source 挂在 wp_enqueue_scripts, 999

  1. 等 Editor.md 完成 jQuery 注销/替换;
  2. 调用 nicen_theme_ensure_jquery():检测是否已注册 jQuery-CDN,统一句柄;
  3. wp_add_inline_script( $handle, 'window.$ = window.jQuery = jQuery;', 'after' )
  4. 所有主题脚本 array( $jq, ... )$in_footer = true

这样保证:jQuery 先就绪,再执行主题的 main.js / monitor.js / index.js


六、修复方案(分层说明)

本次采用 插件侧减少误伤 + 主题侧加固 的组合拳。

6.1 插件:按页面类型加载前台资源(根因收敛)

文件wp-editormd/src/Front/Controller.php

新增 should_enqueue_front_assets()

private function should_enqueue_front_assets() {
    $support_other = trim( (string) Config::get_option( "support_other_text", "editor_basics" ) );
    if ( $support_other !== "" ) {
        return true;  // 用户自定义前台编辑器 ID,保留全站加载
    }
    if ( Config::get_option( "support_front", "editor_basics" ) !== "on" ) {
        return false;
    }
    return is_singular() && comments_open();  // 仅单篇且评论开放
}

enqueue_front_styles / enqueue_front_scripts 开头

if ( ! $this->should_enqueue_front_assets() ) {
    return;  // 首页直接 return,不再 deregister jquery
}

效果:首页不再加载 Editor.md 前台 bundle,从源头避免 jQuery 被无故替换。

同类调整:

模块 调整
FrontStyle\Controller is_singular() 时加载(文内链接新标签页等)
KaTeX::katex_enqueue_scripts is_singular()
MindMap::mindmap_enqueue_scripts is_singular()

6.2 主题:jQuery 句柄统一与加载顺序

文件theme-document-1.2.108/include/themes/load.php

function nicen_theme_jquery_handle() {
    return wp_script_is( 'jQuery-CDN', 'registered' ) ? 'jQuery-CDN' : 'jquery';
}

function nicen_theme_ensure_jquery() {
    $handle = nicen_theme_jquery_handle();
    wp_enqueue_script( $handle );
    wp_add_inline_script( $handle, 'window.$ = window.jQuery = jQuery;', 'after' );
    return $handle;
}
  • 钩子优先级 999nicen_theme_load_source)。
  • mainmain-monitormain-index 等均 array( $jq, ... ) + 页脚加载。
  • 检测到 WP_EDITORMD_VER 时,在全站额外加载主题自带 prism.js(含首页),避免历史版本中 Prism is not defined
  • nicen_theme_editormd_prism_compat()(优先级 1000)仅在 Config_Front 已注册时为其增加 prism 依赖;首页修复后通常不加载 Config_Front,主要靠上一条全站 prism 兜底。

6.3 主题:monitor.js 顶层 $ 防护

文件common/inline/monitor.js

修复前文件顶部:

$.fn.scrollUnique = function () { ... };  // 解析时即需要 $

改为 IIFE:

(function ($) {
    $.fn.scrollUnique = function () { ... };
})(window.jQuery);

即使依赖声明偶发失效,只要 jQuery 已加载,插件方法注册不会拖垮整个文件。

6.4 主题:首页导航不依赖 JS 也能可见(体验兜底)

CSSassets/extra/normal.scss / style.css):

#navigator {
  opacity: 0;
  &.isIndex {
    opacity: 1;  // 首页文章导航
  }
}

内联样式(开启首页导航时):

#space #navigator.isIndex, #space #navigator { opacity: 1; }

即使 JS 仍失败,用户也能看到导航列表(仅固定定位可能不够完美,但不会「整块消失」)。

6.5 主题:目录生成不污染主循环

文件include/functions/common.php

首页 nicen_theme_navigator() 不再对每篇文章 setup_postdata( $post ),改为:

function nicen_theme_can_show_post( $post ) {
    // 用 wp_get_post_categories / wp_get_post_tags 按 ID 判断,不依赖全局 $post
}

避免侧栏渲染阶段改动全局 $post,影响随后 have_posts() 文章列表循环。


七、修复后的资源加载时序(首页)

sequenceDiagram
    autonumber
    participant WP as WordPress
    participant EM as WP Editor.md
    participant TH as Document 主题

    Note over WP,TH: 首页请求 · 修复后

    WP->>+EM: wp_enqueue_scripts(prio 10)
    Note right of EM: should_enqueue_front_assets()<br/>首页 → false
    EM-->>-WP: 跳过 Front/Config<br/>不触碰 jquery

    WP->>+TH: wp_enqueue_scripts(prio 999)
    TH->>TH: nicen_theme_ensure_jquery()
    TH->>TH: enqueue main / monitor / index
    TH->>TH: inline:window.$ = jQuery
    TH->>TH: CSS navigator.isIndex opacity 1
    deactivate TH

    opt 仅当存在 Config_Front 时
        WP->>TH: wp_enqueue_scripts(prio 1000)
        Note right of TH: prism_compat 补依赖
    end

    WP->>WP: 页脚输出脚本
    Note over TH: monitor.js 执行成功<br/>侧栏可见

    Note over EM,TH: PrismJSAuto 仍可能在全站加载 Prism,<br/>但不再伴随会注销 jquery 的 Front bundle

八、自查清单(读者可自行排查)

8.1 浏览器控制台

  • $ is not defined / jQuery is not defined
  • Prism is not defined(若仍有,检查 Prism 与 Config_Front 依赖顺序)

8.2 主题设置

  • 首页显示左侧导航document_show_left_nav)已开启
  • 当前页为博客首页,或「设置 → 阅读」为「最新文章」时的静态首页(is_home() / is_front_page() + show_on_front=posts

8.3 DOM 检查

  • 存在 #space / #navigator.isIndex
  • ul 内有 li > a[href="#h2xxx"]
  • #navigator 计算样式 opacity 为 1

8.4 插件设置

  • 若必须保留全站自定义编辑器 ID(support_other_text),需接受该 ID 所在页仍会加载 Editor 资源
  • 仍有个案时,可开启 Editor.md jQuery 兼容模式

8.5 PHP 层目录为空

若 DOM 根本没有 #space

  • 检查 $wp_query->posts 是否为空;
  • 检查 document_no_display 是否把所有文章分类/标签都排除了;
  • 与 Editor.md 无关,应查查询与主题过滤逻辑。

九、经验总结与最佳实践

9.1 对插件作者

  1. 前台资源按场景加载:评论编辑器只需 is_singular() && comments_open()(及自定义 ID 页),避免全站 wp_deregister_script('jquery')
  2. 避免多处重复注销 jQuery:KaTeX、MindMap、Front、FrontStyle 应共用统一的「前台资源是否加载」判断。
  3. 不要假设全局 $:打包脚本可用 window.jQuery,或保证 deps 正确。

9.2 对主题作者

  1. 所有脚本声明 jquery / 兼容句柄依赖,并尽量页脚加载。
  2. 慎用 opacity: 0 作为唯一显隐手段;关键 UI 应有 CSS 兜底或 noscript 友好。
  3. 侧栏/目录生成避免 setup_postdata 污染主循环;用 WP_Post 对象 + get_the_title( $post )
  4. 与主流 Markdown 插件共存:在 wp_enqueue_scripts 较晚优先级统一 jQuery,并 window.$ = jQuery

9.3 对站点维护者

  • Document + WP Editor.md 可同时使用;若仅需后台 Markdown,可关闭「前台评论 Markdown」,减少冲突面。
  • 排查「导航消失」时,先看控制台,再看 DOM,最后才怀疑 Markdown 解析

十、相关文件索引

角色 路径
首页侧栏模板 theme-document-1.2.108/template/index/sidebar-index-left.php
目录生成 theme-document-1.2.108/include/functions/common.phpnicen_theme_navigator()
脚本加载 theme-document-1.2.108/include/themes/load.php
正文 h2 锚点(HTML 模式) theme-document-1.2.108/include/themes/initialize.phpnicen_theme_fit_html_cat_mode
导航交互 theme-document-1.2.108/common/inline/monitor.js
文章 Mermaid theme-document-1.2.108/include/themes/mermaid.php
插件前台 wp-editormd/src/Front/Controller.php
插件入口 wp-editormd/src/Main.php

十一、结语

WP Editor.md 导致 Document 主题首页文章导航消失,本质不是 Markdown 与目录算法不兼容,而是:

  1. 插件在 不需要编辑器 的页面仍改变 全局 jQuery 注册方式
  2. 主题导航依赖 jQuery 执行显隐/定位,且 CSS 默认 opacity: 0
  3. 脚本错误导致 「有 DOM、看不见」,表象像功能被删。

通过 收敛插件前台加载范围主题统一 jQuery 依赖与优先级CSS/JS 双保险,即可在保留 Markdown 编辑能力的同时恢复首页文章导航。


文档版本:2026-05-27(审校)· 对应 theme-document 1.2.108 与 wp-editormd 10.2.1 修复实践