怎么使用Typecho插件实现添加文章目录

其他教程   发布日期:2023年06月22日   浏览次数:231

本文小编为大家详细介绍“怎么使用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])(.*?)>(.*?)</h1>/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">'."
";
    $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>'."
";
        } elseif ($catalog_depth > $prev_depth) {
          $to_depth++;
          $str .= '<ul class="sub-list">'."
";
        } 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>'."
".'</ul>'."
";
              $to_depth--;
            }
          }
          $str .= '</li>';
        }
      }
      $str .= '<li class="item"><a class="link" href="https://www.19jp.com">

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="https://www.19jp.com">

由于布局和滚动动画的影响,导致锚点定位有点偏差。我们再

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插件实现添加文章目录的资料请关注九品源码其它相关文章!