
本文详解如何将原本作用于 the_content 过滤器的动态目录生成函数,重构为功能完整、可复用的 WordPress 短代码,解决直接调用 add_shortcode() 时无输出的问题。
本文详解如何将原本作用于 `the_content` 过滤器的动态目录生成函数,重构为功能完整、可复用的 wordpress 短代码,解决直接调用 `add_shortcode()` 时无输出的问题。
WordPress 中常见的做法是通过 the_content 过滤器自动为文章内容插入目录(TOC),但这种方式缺乏灵活性——无法按需启用、无法控制插入位置、也不支持页面或自定义区块。要将其转换为短代码(如 [toc_content]),关键在于理解短代码回调函数的参数签名与执行上下文差异。
原函数 create_toc($html) 是为 the_content 过滤器设计的:它接收已渲染的文章 HTML 字符串作为唯一参数,并在处理后返回“目录 HTML + 修改后的全文 HTML”。而短代码回调函数签名固定为:
function my_shortcode_callback($atts = [], $content = null, $tag = '') { ... }
其中:
- $atts:短代码属性数组(如 [toc_content depth="3"] → ['depth' => '3']);
- $content:闭合型短代码内的内容(如 [toc_content]Hello[/toc_content] 中的 "Hello");
- $tag:短代码名称(本例中为 'toc_content')。
因此,直接复用原函数会导致 $html 实际接收的是空数组 $atts,后续 DOM 解析必然失败,最终返回空字符串。
✅ 正确做法是:分离逻辑,创建专用于短代码的新函数,该函数主动获取当前文章内容、解析并生成 TOC,再返回完整 HTML(不含原始内容)——因为短代码本身不负责包裹内容,而是「插入到内容中某处」。
以下是推荐的重构实现:
function create_toc_shortcode($atts = [], $content = null, $tag = '') {
// 仅在单篇文章上下文中执行
if (!is_single() || !in_the_loop()) {
return '';
}
// 获取当前文章内容(注意:需确保已加载全局 $post)
global $post;
$html = apply_filters('the_content', $post->post_content);
// 使用 DOMDocument 解析(同原逻辑,但更健壮)
$dom = new DOMDocument();
libxml_use_internal_errors(true);
$dom->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
libxml_clear_errors();
$toc = '<div class="toc-bound">
<div class="toc-ctr">Table of Contents</div>
<ul class="toc">';
$i = 1;
$xpath = new DOMXPath($dom);
$expression = '//*[contains(concat(" ", normalize-space(@class), " "), " toc-item ")]';
foreach ($xpath->evaluate($expression) as $element) {
$id = 'toc-' . $i;
$element->setAttribute('id', $id);
$toc .= sprintf(
'<li><a href="%s#%s">%s</a></li>',
esc_url(get_the_permalink()),
esc_attr($id),
esc_html($element->textContent)
);
$i++;
}
$toc .= '</ul>
</div>';
return $toc;
}
add_shortcode('toc_content', 'create_toc_shortcode');
? 重要注意事项:
- ✅ 不要在短代码中返回 $dom->saveHTML():这会输出整页 HTML(含 等),造成结构混乱;我们只需返回纯 TOC HTML。
- ✅ 确保 toc-item 元素已存在于文章内容中(例如编辑器中手动添加 class="toc-item" 的
或
),否则 XPath 查询无结果。- ⚠️ 避免重复执行:若同时启用 the_content 过滤器和短代码,会导致 TOC 出现两次。建议停用原 add_filter('the_content', 'create_toc'),统一通过 [toc_content] 控制。
- ? 安全防护:使用 esc_url()、esc_attr()、esc_html() 对动态值转义,防止 XSS。
- ? 进阶扩展:可通过 $atts 支持 exclude="h3,h4" 或 title="My TOC" 等参数,进一步提升实用性。
最后,在文章编辑器中任意位置插入 [toc_content],即可在该处渲染目录。配合 CSS 样式(如 .toc-bound { margin: 2em 0; } .toc li { margin: 0.3em 0; }),即可获得专业、可控的响应式目录体验。











