Impeka - Premium WordPress Multipurpose theme by Greatives

给 WordPress 文章添加自动目录(免插件)

文章目录
  1. WordPress 构建文章多级目录索引
  2. WordPress 文章目录的 CSS 样式表
  3. WordPress 文章自动目录所需要的 JS 代码

在写文章的时候咱为了结构更清晰,通常会添加h2-h6标签来展示段落标题,同时也提醒网络蜘蛛这些是内文小标题,是文章的重点。特别是当 WordPress 文章较长时,有一个自动索引的目录能有效帮助访客快速了解内容结构,会大大提高读者的阅读体验。

给 WordPress 文章添加自动目录的方法非常多,官方库里边也有很多相关的目录插件,比如知名的 LuckyWP Table of Contents、Heroic Table of Contents、Rich Table of Contents 等,它们主体功能差不多,文件越大的越贵的自然是功能更完善,小细节更好,但副作用也很明显,它们配置麻烦且会加重主机负担影响页面加载速度。

此前有段时间我特想给 WordPress 添加一个文章目录,后来因为更新较少就放下了。正好最近在瞎折腾,也看了不少教程,最后发现微言微语这一篇相当良心,该方案代码结构清晰注释多,操作步骤明了,作为技术小白的我一下就折腾成功了。为了给更多的人看到,特地把这篇纯代码实现 WordPress 多级文章目录索引转载过来,也算作是我的一个备忘录吧。

本方案代码量少,可自定义程度也比较高,我在原基础上稍微调整了下 CSS,具体的实现效果看本页,根据喜好你也可以修改 CSS 把目录嵌入进正文中展示,后面本站可能会那样做,届时再把 CSS 贴出来。其实,我的理想方案是把文章目录嵌入到边栏小工具中,并能在文章编辑时开关控制…这是后话了。

WordPress 构建文章多级目录索引

将以下这段代码加载进function.php循环中,通过代码中注释可以知道文章目录是获取h2-h6的标题,同时 h 标签的个数大于 2 才生效,也就是文章中要有 3 个及以上才展示,该数量可以自定义修改。

这段使用了两个函数:buildDirectory 和 article_index。article_index 函数是一个 WordPress 的过滤器函数,用于在文章内容中自动生成目录。buildDirectory 函数则是用来构建目录的核心逻辑。

根据代码,article_index 函数首先使用正则表达式匹配文章内容中的标题,然后根据匹配到的标题内容和标题级别调用 buildDirectory 函数来构建目录。最后,将生成的目录插入到文章内容中,并返回处理后的内容。这段代码有个小 Bug,即当第一个 H 标签比后面的小时,目录可能无法正常加载…当然这也提醒我们:写作要规范~

// 构建文章多级目录索引
function buildDirectory($titleIndexArr, $titleContentArr, &$index, &$html)
{
    $size = sizeof($titleIndexArr);
    $flag = $index == 0;
    $html .= $flag ? '<ol id="index-ol">' : '<ul id="index-ul">';
    while ($index < $size) {
        $title = trim(strip_tags($titleContentArr[$index])); // 标题内容
        $h = $titleIndexArr[$index]; // 几级标题
        $html .= '<li><a href="#title-' . $index . '" title="' . $title . '">' . $title . "</a></li>";
        if ($index == $size - 1) { //最后一个
            $index++;
            break;
        }
        $next_h = $titleIndexArr[++$index]; // 下个标题是几级标题
        if ($h < $next_h) { // 子标题递归
            buildDirectory($titleIndexArr, $titleContentArr, $index, $html);
            if ($index >= $size || $h > $titleIndexArr[$index]) {
                break;
            }
        } else if ($h > $next_h) {
            break;
        }
    }
    $html .= $flag ? '</ol>' : '</ul>';
}
function article_index($content)
{
    $html = '';
    $matches = array();
    // 当前设置 [2-6]即 h2 到 h6 可以自己修改下面正则
    $r = '/<h([2-6])(.*?)>(.*?)<\/h[2-6]>/is';
    // preg_match_all 函数用于执行一个全局正则表达式匹配。$r=正则,$content=文章内容,$matches=作为输出参数输出所有匹配结果
    preg_match_all($r, $content, $matches);
    $num_match = count($matches[0]);
    // 用于文章页,同时 h 标签个数大于 2 个才生效
    if ( is_single() && ($num_match > 2) ) {
        foreach ($matches[1] as $key => $value) {
            // strip_tags() 函数剥去字符串中的 HTML、XML 以及 PHP 的标签
            $title = trim(strip_tags($matches[3][$key])); //文章标题
            // 文章原标题添加锚点
            $content = str_replace($matches[0][$key], '<h' . $value . ' id="title-' . $key . '"' . $matches[2][$key] . '>' . $title . '</h' . $value . '>', $content);
        }
        $titleIndexArr = $matches[1];
        $titleContentArr = $matches[3];
        $index = 0;
        $html .= "\n".'<div id="article-index-show">目 录</div><div id="article-index"><div id="article-index-top"><strong>文章目录</strong><span id="article-index-hide">隐藏</span></div>';
        buildDirectory($titleIndexArr, $titleContentArr, $index, $html);
        $html .= '</div>'."\n";
    }
    return $html. $content;
}
add_filter('the_content', 'article_index');

WordPress 文章目录的 CSS 样式表

以下这段样式代码中包含了上述 WordPress 文章自动目录的一些样式定义,如索引框的位置、大小、背景颜色等。同时还包括了显示和隐藏文章目录的按钮样式。另外,这是我第一次用box-shadow: 0 0 5px rgba(0,0,0,.4);这个参数,它能让内容产生阴影,可以给予页面更多灵活性,还挺好玩的~

#article-index {
	position: fixed;
	top: 50%;
	transform: translateY(-50%);
	left: 0;
	width: auto;
	max-height: 76%;
	padding: 0 10px;
	font-size: 14px;
	border-radius: 0 6px 6px 0;
	background-color: #fff;
	box-shadow: 0 0 5px rgba(0, 0, 0, .4);
	overflow: auto;
	z-index: 99;
	display: none;
}

#article-index-top {
	position: sticky;
	top: 0;
	z-index: 92;
	background: #fff;
}

#article-index strong {
	display: block;
	font-size: 16px;
	padding: 10px 0 12px 5px;
}

#index-ol {
	list-style: none;
	font-size: 18px;
}

#index-ol ul {
	font-size: 15px;
}

#index-ol li {
	padding: 0;
	margin-left: 18px;
	line-height: 24px;
	position: relative;
	list-style-position: inherit;
	word-break: break-all;
}

#index-ul {
	list-style: revert-layer;
	margin: 0;
	padding: 4px 0px 6px 36px;
}

#index-ul li:before {
	display: none;
}

#article-index-show {
	position: fixed;
	top: 50%;
	transform: translateY(-50%);
	left: 0;
	display: block;
	width: 50px;
	height: 36px;
	line-height: 36px;
	text-align: center;
	font-size: 14px;
	/* border-radius: 0 36px 36px 0; */
	color: #fff;
	background-color: #cc3333;
	cursor: pointer;
}

#article-index-hide {
	position: absolute;
	right: 0;
	top: 5px;
	display: block;
	width: 32px;
	height: 32px;
	line-height: 32px;
	text-align: center;
	font-size: 12px;
	border-radius: 100%;
	background-color: #eeeeee;
	cursor: pointer;
}

#article-index-hide:hover {
	color: #fff;
	background-color: #cc3333;
}

以上这个样式是我这里正在使用的效果,以下这段 CSS 是上面这段的变体,它让代码默认展开悬浮在正文旁边(细节还需要自己改一些),如图所示:

给 WordPress文章添加自动目录(免插件)
给 WordPress 文章添加自动目录(免插件)
#article-index {
    position: fixed;
    top: 30%;
    transform: translateY(-20%);
    margin-left: -240px;
    width: 224px;
    max-height: 100%;
    padding: 0 10px;
    font-size: 14px;
    border-radius: 6px;
    background-color: #fff;
    box-shadow: 0 0 5px rgba(0, 0, 0, .4);
    overflow: auto;
    z-index: 99;
    display: block;
}

#article-index strong {
    display: block;
    font-size: 16px;
    padding: 10px 0 16px 0;
}

#index-ol {
    list-style: square;
    text-wrap: 
wrap;
}

#index-ol a {
    color: #555;
}

#index-ol li {
    list-style: none;
    padding: 0;
    margin-left: -50px;
    line-height: 24px;
    position: relative;
    list-style-position: inherit;
    word-break: break-all;
}

#index-ul {
    list-style: circle;
    margin: 0;
    padding: 5px 0 5px 8px;
}

#index-ul li:before {
    display: none;
}

#article-index-show {
    position: fixed;
    top: 31%;
    transform: translateY(-50%);
    margin-left: -240px;
    display: block;
    width: 200px;
    height: 36px;
    line-height: 36px;
    text-align: center;
    font-size: 14px;
    border-radius: 36px 36px 36px 36px;
    color: #fff;
    background-color: #59f;
    cursor: pointer;
}

#article-index-hide {
    position: absolute;
    right: 5px;
    top: 5px;
    display: block;
    color: #fff;
    width: 32px;
    height: 32px;
    line-height: 32px;
    text-align: center;
    font-size: 12px;
    border-radius: 100%;
    background-color: #59f;
    cursor: pointer;
}

#article-index-hide:hover {
    color: #fff;
    background-color: #0088dd;
}

WordPress 文章自动目录所需要的 JS 代码

根据上面的主函数和 CSS,这里还需要一些 JavaScript 代码来实现点击显示和隐藏文章目录的功能。以下是一个简单的示例 JavaScript 代码,由 Devv AI 自动生成,用于显示和隐藏文章目录索引:

// 获取显示和隐藏按钮元素
const showButton = document.getElementById('article-index-show');
const hideButton = document.getElementById('article-index-hide');
const indexElement = document.getElementById('article-index');

// 点击显示按钮显示文章目录索引
showButton.addEventListener('click', function() {
    indexElement.style.display = 'block';
});

// 点击隐藏按钮隐藏文章目录索引
hideButton.addEventListener('click', function() {
    indexElement.style.display = 'none';
});

这段代码尚未经过测试,建议使用后面一段!请确保将此 JavaScript 代码添加到您的页面中,并根据需要调整元素 ID 以匹配您的实际代码。这样,您就可以实现点击按钮显示和隐藏文章目录索引的功能。

以下这段代码是本站正在使用的 JS 效果,把它加载到 jQuery 库后边就行,它能控制文章目录的开关和平滑滚动,可根据需要自行设定数值控制打开速度(毫秒)。

//文章目录展示切换
jQuery(document).ready(function(){
	jQuery("#article-index-hide").click(function(){
		jQuery("#article-index").hide(100);
		jQuery("#article-index-show").show(200);
	});
	jQuery("#article-index-show").click(function(){
		jQuery("#article-index").show(200);
		jQuery("#article-index-show").hide(100);
	});
});

//文章目录锚点点击平滑滚动
jQuery(document).on('click', '#article-index a[href^="#"]', function(e) {
	var id = jQuery(this).attr('href');
	var $id = jQuery(id);
	if ($id.length === 0) {
		return;
	}
	e.preventDefault();
	var pos = $id.offset().top;
	jQuery('body, html').animate({scrollTop: pos});
});
918 619 Kevin's
「给 WordPress 文章添加自动目录(免插件)」有 14 条评论
  • Lvtu
    04/17/2024 at 18:07 回复

    你这个站适合放在右边,这样不会挡住内容。。。

  • […] 最近在折腾本WordPress小站,在AI的帮助下已经陆陆续续实现了文章自建目录、EXIF信息展示、灯箱插件修复、主动保留访客Cookie等功能…某天深夜我注意到,各大搜索引擎都没有录入本站Logo,难道是用了美元$符号?遂我请chatgpt帮忙设计一个Logo,只是可惜,它说他仅是一款文字AI,不会画图。 […]

  • 平安家属子痕
    03/30/2024 at 22:44 回复

    如果能侧栏一起联动就好了。

    • Kevin
      04/01/2024 at 22:10 回复

      后面有机会搞一搞~

  • cion
    03/29/2024 at 19:13 回复

    我用的是ToC插件,但是也不太满意,大佬能不能折腾一个类似少数派的那种侧边目录导航(举个例子https://sspai.com/post/87150)?浏览文章时隐藏成小横条和只显示当前所在章节的名称,并且是固定在文章页面右侧而不是在文章开头的静态位置,然后鼠标hover上去时再显示完整的章节目录。检查元素是right-side-directory,不像是插件,可能是他们自己自定义的代码。

    • Kevin
      04/01/2024 at 22:10 回复

      你这需求和上面的一样,
      不过我想了一下这个东西有点儿超出我的技术范畴了,应该说太超了,暂时可能搞不定

  • 两对半
    03/29/2024 at 10:15 回复

    博客变化真大,一下没认出来,还以为是哪位技术员的博客呢。

    在写评论时,里面的“要不如?和大家一起聊聊”没消掉,看上去有重影,填昵称、邮件、地址的地方也是如此。可能还在调试吧

    • Kevin
      03/29/2024 at 12:11 回复

      确实是在调试中,我分析了下,恰好是本页贴上来的一些代码和主体原有的JS冲突了

      不过这一点我暂时也不知道如何解决,囧

  • 威言威语
    03/29/2024 at 09:46 回复

    哈哈,我好早之前的文章,这个功能我自己也一直在用。

    • Kevin
      04/01/2024 at 22:09 回复

      nice code,再次感谢

  • 1900
    03/28/2024 at 21:58 回复

    我昨天也在写这个功能,用ChatGPT胡了一个,只支持了H2和H3
    ```

    const $ = cheerio.load(html);
    let toc = "";
    let currentH2 = null;
    let currentH3 = null;

    $("h2, h3, h4").each((i, el) => {
    const tagName = $(el).prop("tagName");
    const id = $(el).attr("id");
    const title = $(el).text();

    if (tagName === "H2") {
    if (currentH2) {
    if (currentH3) {
    toc += "";
    currentH3 = null;
    }
    toc += "";
    }
    toc += `${title}`;
    currentH2 = id;
    } else if (tagName === "H3") {
    if (currentH3) {
    toc += "";
    }
    toc += `${title}`;
    currentH3 = id;
    } else if (tagName === "H4") {
    toc += `${title}`;
    }
    });

    if (currentH3) {
    toc += "";
    }
    if (currentH2) {
    toc += "";
    }
    toc += "";
    ```

    • Kevin
      04/01/2024 at 22:09 回复

      我让 AI帮我改错可以,完全重造一个我看不懂,害

  • obaby
    03/28/2024 at 17:03 回复

    这个功能高级

    • Kevin
      04/01/2024 at 21:46 回复

      按需添加罢了。。。比起你们程序员来说就是不入流

发表评论

请输入关键词…