这篇文章主要介绍了如何使用Typecho插件实现添加文章目录的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇如何使用Typecho插件实现添加文章目录文章都会有所收获,下面我们一起来看看吧。
注意:我使用的是Joe主题7.3,其他主题文件路径可能不一样。
1.声明 createAnchor 函数
在 core/functions.php
中添加如下代码:
// 添加文章标题锚点 function createAnchor($obj) { global $catalog; global $catalog_count; $catalog = array(); $catalog_count = 0; $obj = preg_replace_callback('/<h([1-4])(.*?)>(.*?)<\/h\1>/i', function($obj) { global $catalog; global $catalog_count; $catalog_count ++; $catalog[] = array('text' => trim(strip_tags($obj[3])), 'depth' => $obj[1], 'count' => $catalog_count); return '<h'.$obj[1].$obj[2].' id="cl-'.$catalog_count.'">'.$obj[3].'</h'.$obj[1].'>'; }, $obj); return $obj; }
也可以在标题元素内添加 <a>
标签,然后该标签新增 id
属性。
createAnchor
函数主要是通过正则表达式替换文章标题H1~H4来添加锚点,接下来我们需要调用它。
2.调用函数
同样在 core/core.php
中的 themeInit
方法最后一行之前添加如下代码:
if ($self->is('single')) { $self->content = createAnchor($self->content); }
现在可以查看一下文章详情页面的源代码。文章的 H1~H4
元素应该添加了诸如 cl-1
、cl-2
之类的 id
属性值。具体啥名不是关键,好记就行。
1.声明 getCatalog 函数
在 core/functions.php
中添加如下代码:
// 显示文章目录 function getCatalog() { global $catalog; $str = ''; if ($catalog) { $str = '<ul class="list">'."\n"; $prev_depth = ''; $to_depth = 0; foreach($catalog as $catalog_item) { $catalog_depth = $catalog_item['depth']; if ($prev_depth) { if ($catalog_depth == $prev_depth) { $str .= '</li>'."\n"; } elseif ($catalog_depth > $prev_depth) { $to_depth++; $str .= '<ul class="sub-list">'."\n"; } else { $to_depth3 = ($to_depth > ($prev_depth - $catalog_depth)) ? ($prev_depth - $catalog_depth) : $to_depth; if ($to_depth3) { for ($i=0; $i<$to_depth3; $i++) { $str .= '</li>'."\n".'</ul>'."\n"; $to_depth--; } } $str .= '</li>'; } } $str .= '<li class="item"><a class="link" href="#cl-'.$catalog_item['count'].'" rel="external nofollow" title="'.$catalog_item['text'].'">'.$catalog_item['text'].'</a>'; $prev_depth = $catalog_item['depth']; } for ($i=0; $i<=$to_depth; $i++) { $str .= '</li>'."\n".'</ul>'."\n"; } $str = '<section class="toc">'."\n".'<div class="title">文章目录</div>'."\n".$str.'</section>'."\n"; } echo $str; }
getCatalog
方法通过递归 $catalog
数组生成文章目录,接下来我们需要调用它。
2.函数
最好将放在右侧边栏中。为此在 public/aside.php
中添加如下代码:
<?php if ($this->is('post')) getCatalog(); ?>
注意:只有文章才使用目录,独立页面那些不需要,所以加了判断。Typecho 有一些神奇的 is
语法可以方便二次开发,可以访问它的官网文档了解更多。
现在点击右侧的文章目录,可以滚动到相应的文章小标题位置了。
可以看到,当前的文章目录还比较丑陋,我们来美化一下。在 assets/css/joe.post.min.scss
中添加如下 SCSS 代码:
.joe_aside { .toc { position: sticky; top: 20px; width: 250px; background: var(--background); border-radius: var(--radius-wrap); box-shadow: var(--box-shadow); overflow: hidden; .title { display: block; border-bottom: 1px solid var(--classA); font-size: 16px; font-weight: 500; height: 45px; line-height: 45px; text-align: center; color: var(--theme); } .list { padding-top: 10px; padding-bottom: 10px; max-height: calc(100vh - 80px); overflow: auto; .link { display: block; padding: 8px 16px; border-left: 4px solid transparent; color: var(--main); text-decoration: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; &:hover { background-color: var(--classC); } &.active { border-left-color: var(--theme); } } } } }
为了方便操作,将 .toc
设置成 position: sticky;
实现了吸顶定位。考虑到文章目录可能很多,为 .toc
列表添加了 overflow: auto;
,如代码第 3 ~ 4
行。
由于 .joe_header
(主题标头)也使用了吸顶定位,导致和文章目录有遮挡,所有加了 has_toc .joe_header
来取消页面主题标头的吸顶功能,如下代码:
.has_toc { .joe_header { position: relative; } }
要显示文章目录当前选中项的状态,需要用到 JavaScript 给选中项添加一个 active
样式。在 assets/js/joe.post_page.js
中添加如下代码:
var headings = $('.joe_detail__article').find('h2, h3, h4, h5'); var links = $('.toc .link'); var tocList = document.querySelector('.tocr > .list'); var itemHeight = $('.toc .item').height(); var distance = tocList.scrollHeight - tocList.clientHeight; var timer = 0; // 是否自动滚动 var autoScrolling = true; function setItemActive(id) { links.removeClass('active'); var link = links.filter("[href='#" + id + "']") link.addClass('active'); } function onChange() { autoScrolling = true; if (location.hash) { id = location.hash.substr(1); var heading = headings.filter("[id='" + id + "']"); var top = heading.offset().top - 15; window.scrollTo({ top: top }) setItemActive(id) } } window.addEventListener('hashchange', onChange); // hash没有改变时手动调用一次 onChange();
由于布局和滚动动画的影响,导致锚点定位有点偏差。我们再 setItemActive
函数中用 scrollTo
或 scrollIntoView
来纠正。另外,我们希望有锚点的链接可以直接定位,因此监听了 hashchange
事件。点击文章目录测试一下定位,再手动键入锚点测试一下,应该都没啥问题。
目前可以从文章目录定位到文章标题了,是单向定位,双向定位还需要实现滚动文章内容时定位到文章目录的当前项。正如我们马上能想到的,需要监听 window
的 scroll
事件,如下代码:
function onScroll() { if (timer) { clearTimeout(timer); } timer = setTimeout(function () { var top = $(window).scrollTop(); var count = headings.length; for (var i = 0; i < count; i++) { var j = i; // 滚动和点击时 index 相差 1,需要 autoScrolling 来区分 if (i > 0 && !autoScrolling) { j = i - 1; } var headingTop = $(headings[i]).offset().top; var listTop = distance * i / count // 判断滚动条滚动距离是否大于当前滚动项可滚动距离 if (headingTop > top) { var id = $(headings[j]).attr('id'); setItemActive(id); // 如果目录列表有滑条,使被选中的下一元素可见 if (listTop > 0) { // 向上滚动 if (listTop < itemHeight) { listTop -= itemHeight; } else { listTop += itemHeight; } $(tocList).scrollTop(listTop) } break; } else if (i === count - 1) { // 特殊处理最后一个元素 var id = $(headings[i]).attr('id'); setItemActive(id); if (listTop > 0) { $(tocList).scrollTop(distance) } } } autoScrolling = false; }, 100); } $(window).on('scroll', onScroll);
首先,在 onScroll
事件处理函数中遍历标题数组 headings
, 如果滚动条滚动距离 top
大于当前标题项 item
可滚动距离 headingTop
,再调用 setItemActive
函数,传入当前的标题项的 id
来判断文章目录激活状态。
如果目录列表有滑条,调用 jQuery 的 scrollTop
方法滚动目录列表滑条,使被选中目录项的上下元素可见,
现在文章目录基本上可用了,也还美观,后续可以考虑优化再封装成一个插件。
关于“如何使用Typecho插件实现添加文章目录”这篇文章的内容就介绍到这里,感谢各位的阅读!相信大家对“如何使用Typecho插件实现添加文章目录”知识都有一定的了解,大家如果还想学习更多知识,欢迎关注亿速云行业资讯频道。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。