Typecho插件實現(xiàn)添加文章目錄的方法詳解
我的長博文不少,比較影響閱讀體驗,有必要添加一個文章目錄功能。相比 Wordpress, Typecho 的插件就比較少了。我想找一個像掘金那樣為文章添加目錄的插件,沒一個合適的。此類教程也不是很多,而且差不多都是前臺 JavaScript 來實現(xiàn)的,感覺這樣不如后臺實現(xiàn)來的好。
注意:我使用的是Joe主題7.3,其他主題文件路徑可能不一樣。
添加文章標題錨點
1.聲明 createAnchor 函數(shù)
在 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;
}也可以在標題元素內(nèi)添加 <a> 標簽,然后該標簽新增 id 屬性。
createAnchor 函數(shù)主要是通過正則表達式替換文章標題H1~H4來添加錨點,接下來我們需要調(diào)用它。
2.調(diào)用函數(shù)
同樣在 core/core.php 中的 themeInit 方法最后一行之前添加如下代碼:
if ($self->is('single')) {
$self->content = createAnchor($self->content);
}現(xiàn)在可以查看一下文章詳情頁面的源代碼。文章的 H1~H4 元素應該添加了諸如 cl-1、cl-2 之類的 id 屬性值。具體啥名不是關(guān)鍵,好記就行。
顯示文章目錄
1.聲明 getCatalog 函數(shù)
在 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_depth2 = ($to_depth > ($prev_depth - $catalog_depth)) ? ($prev_depth - $catalog_depth) : $to_depth;
if ($to_depth2) {
for ($i=0; $i<$to_depth2; $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 數(shù)組生成文章目錄,接下來我們需要調(diào)用它。
2.函數(shù)
最好將放在右側(cè)邊欄中。為此在 public/aside.php 中添加如下代碼:
<?php if ($this->is('post')) getCatalog(); ?>
注意:只有文章才使用目錄,獨立頁面那些不需要,所以加了判斷。Typecho 有一些神奇的 is 語法可以方便二次開發(fā),可以訪問它的官網(wǎng)文檔了解更多。
現(xiàn)在點擊右側(cè)的文章目錄,可以滾動到相應的文章小標題位置了。
添加文章目錄樣式
可以看到,當前的文章目錄還比較丑陋,我們來美化一下。在 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; 實現(xiàn)了吸頂定位??紤]到文章目錄可能很多,為 .toc 列表添加了 overflow: auto;,如代碼第 3 ~ 4 行。
由于 .joe_header(主題標頭)也使用了吸頂定位,導致和文章目錄有遮擋,所有加了 has_toc .joe_header 來取消頁面主題標頭的吸頂功能,如下代碼:
.has_toc {
.joe_header {
position: relative;
}
}定位到文章
要顯示文章目錄當前選中項的狀態(tài),需要用到 JavaScript 給選中項添加一個 active 樣式。在 assets/js/joe.post_page.js 中添加如下代碼:
var headings = $('.joe_detail__article').find('h1, h2, h3, h4');
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沒有改變時手動調(diào)用一次
onChange();由于布局和滾動動畫的影響,導致錨點定位有點偏差。我們再 setItemActive 函數(shù)中用 scrollTo 或 scrollIntoView 來糾正。另外,我們希望有錨點的鏈接可以直接定位,因此監(jiān)聽了 hashchange 事件。點擊文章目錄測試一下定位,再手動鍵入錨點測試一下,應該都沒啥問題。
定位到目錄
目前可以從文章目錄定位到文章標題了,是單向定位,雙向定位還需要實現(xiàn)滾動文章內(nèi)容時定位到文章目錄的當前項。正如我們馬上能想到的,需要監(jiān)聽 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 來區(qū)分
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 事件處理函數(shù)中遍歷標題數(shù)組 headings, 如果滾動條滾動距離 top 大于當前標題項 item 可滾動距離 headingTop,再調(diào)用 setItemActive 函數(shù),傳入當前的標題項的 id 來判斷文章目錄激活狀態(tài)。
如果目錄列表有滑條,調(diào)用 jQuery 的 scrollTop 方法滾動目錄列表滑條,使被選中目錄項的上下元素可見,
現(xiàn)在文章目錄基本上可用了,也還美觀,后續(xù)可以考慮優(yōu)化再封裝成一個插件。
吐槽一下:Joe 主題太依賴jQuery了,修改起來費勁 ::(汗)。
到此這篇關(guān)于Typecho插件實現(xiàn)添加文章目錄的方法詳解的文章就介紹到這了,更多相關(guān)Typecho添加文章目錄內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
PHP實現(xiàn)把數(shù)字ID轉(zhuǎn)字母ID
以下是對使用PHP把數(shù)字ID轉(zhuǎn)字母ID的實現(xiàn)代碼進行了詳細的分析介紹,需要的朋友可以過來參考下2013-08-08
windows服務器中檢測PHP SSL是否開啟以及開啟SSL的方法
這篇文章主要介紹了windows服務器中檢測PHP SSL是否開啟以及開啟SSL的方法,需要的朋友可以參考下2014-04-04

