
本文详解如何将基于 the_content 过滤器实现的动态目录逻辑,安全、正确地重构为可复用的 WordPress 短代码,解决 add_shortcode() 调用失败问题,并提供完整可运行代码与关键注意事项。
本文详解如何将基于 `the_content` 过滤器实现的动态目录逻辑,安全、正确地重构为可复用的 wordpress 短代码,解决 `add_shortcode()` 调用失败问题,并提供完整可运行代码与关键注意事项。
WordPress 中,将目录(TOC)生成逻辑从 the_content 过滤器迁移到短代码时,核心误区在于混淆了过滤器与短代码的参数签名和执行时机。原函数 create_toc($html) 是为 the_content 设计的:它接收已渲染的文章 HTML 字符串并返回修改后的 HTML。而短代码回调函数必须遵循固定签名:function my_shortcode($atts, $content = null, $tag) {},且其职责是主动构建并返回 HTML 内容,而非处理传入的 $html。
因此,需彻底重构逻辑——不再依赖外部 $html 输入,而是主动获取当前文章内容、解析 DOM、提取 .toc-item 元素,并生成独立的 TOC 结构。以下是推荐实现:
function create_toc_shortcode($atts, $content = null, $tag) {
// 仅在单篇文章上下文中执行
if (!is_single() || !in_the_loop()) {
return '';
}
// 获取当前文章内容(不含其他过滤器干扰,避免循环)
global $post;
$post_content = $post->post_content;
// 初始化 DOM 解析器
$dom = new DOMDocument();
libxml_use_internal_errors(true);
$dom->loadHTML(mb_convert_encoding($post_content, 'HTML-ENTITIES', 'UTF-8'));
libxml_clear_errors();
// 构建 TOC 容器
$toc_html = '<div class="toc-bound">
<div class="toc-ctr">Table of Contents</div>
<ul class="toc">';
$xpath = new DOMXPath($dom);
$expression = '//*[contains(concat(" ", normalize-space(@class), " "), " toc-item ")]';
$toc_items = $xpath->evaluate($expression);
$i = 1;
foreach ($toc_items as $element) {
$text = trim($element->textContent);
if (empty($text)) continue;
// 为原文本元素添加唯一 ID(确保锚点可用)
$element->setAttribute('id', 'toc-' . $i);
$toc_html .= sprintf(
'<li><a href="%s#toc-%d">%s</a></li>',
esc_url(get_the_permalink()),
$i,
esc_html($text)
);
$i++;
}
$toc_html .= '</ul>
</div>';
// 重要:不修改原始 post_content,仅返回 TOC HTML
// 若需同步为原文本元素加 ID,应使用独立钩子(如 the_content 后置处理),避免短代码内修改全局状态
return $toc_html;
}
add_shortcode('toc_content', 'create_toc_shortcode');
✅ 使用方式:在文章编辑器中插入 [toc_content] 即可显示目录。
⚠️ 关键注意事项:
- 不要在短代码中调用 the_content 过滤器或修改 $post->post_content:这会引发递归、重复 ID 或内容污染;
- ID 唯一性保障:上述代码为 .toc-item 元素动态添加 id="toc-X",但若原文已含 ID 或存在多个短代码实例,需引入哈希或上下文前缀(如 toc-{$post_id}-{$i});
- 性能优化建议:对高流量站点,可结合 wp_cache_get/set 缓存解析结果;
- 前端样式与平滑滚动:需额外添加 CSS(如 .toc-bound { margin-bottom: 2em; })及 JS(监听点击并 event.preventDefault(); scrollTo({ ... }))提升体验。
该方案解耦清晰、语义明确,既满足短代码按需插入的灵活性,又保持 DOM 操作的安全性与可维护性。











