又折腾博客了,之前使用WPJAM插件文实现章目录功能,发现大部分WordPress博客都在使用WPJAM的文章目录,使用起来简单方便。
适用于WPJAM文章目录的js、css代码
https://feng.pub/1020235787.html
WPJAM的文章目录可以单独设置是否关闭文章目录功能,也可以单独设置文章目录显示到的级别,这一点很灵活。但WPJAM采用后端PHP构建文章目录的HTML结构,我在想是否可以让前端来实现,于是着手做这件事情,现把代码分享出来。
效果图
CSS代码
根据需要可以修改CSS代码。
/**
前端文章目录 - css
author: 阿锋
link: https://feng.pub
version: 0.0.3
*/
.content_toc {
display: block;
width: 200px;
overflow: hidden;
overflow-y: auto;
position: fixed;
bottom: 164px;
right: 15px;
padding: 0px;
margin: 0px;
background-color: var(--theme-palette-color-8);
border-radius: 5px;
box-shadow: 0 0 10px var(--theme-palette-color-1);
scrollbar-width: none;
-ms-overflow-style: none;
z-index: 1;
transition: all 0.5s;
}
.content_toc::-webkit-scrollbar {
display: none;
}
.content_toc.less {
width: 40px;
height: 40px;
color: var(--theme-palette-color-1);
background-color: var(--theme-palette-color-1);
border-radius: 20px;
box-shadow: none;
overflow: hidden;
}
.content_toc.less .content_toc_title h6,
.content_toc.less .content_toc_main {
display: none;
}
.content_toc_title {
position: relative;
top: 0;
left: 0;
right: 0;
height: 40px;
padding: 0;
margin: 0;
transition: all 0.5s;
}
.content_toc_title h6 {
padding-left: 10px;
color: var(--theme-palette-color-1);
font-size: 18px;
font-weight: 700;
line-height: 40px;
}
.content_toc_title h6::before {
content: '|';
margin-right: 10px;
color: var(--theme-palette-color-1);
background: var(--theme-palette-color-1);
border-radius: 5px;
}
.content_toc_title .btn {
position: absolute;
width: 20px;
height: 20px;
top: 10px;
right: 10px;
line-height: 20px;
font-weight: bold;
color: var(--theme-palette-color-7);
background-color: var(--theme-palette-color-1);
text-align: center;
font-size: 12px;
border-radius: 50%;
box-shadow: 0 0 10px var(--theme-palette-color-6);
cursor: pointer;
transition: all 0.5s;
}
.content_toc_title .btn:hover,
.content_toc_title .btn.close:hover {
color: var(--theme-palette-color-8);
background-color: var(--theme-palette-color-2);
box-shadow: 0 0 10px var(--theme-palette-color-1);
}
.content_toc_title .btn.close {
top: 0;
right: 0;
width: 40px;
height: 40px;
line-height: 40px;
border-radius: 20px;
color: var(--theme-palette-color-7);
background-color: var(--theme-palette-color-1);
}
.content_toc_main {
padding: 0;
margin: 0;
overflow-y: auto;
transition: all 0.5s;
}
.content_toc_main::-webkit-scrollbar {
display: none;
}
.content_toc_tree {
padding: 0 10px 10px 10px;
margin: 0;
list-style: none;
}
.content_toc_tree li {
position: relative;
width: 170px;
font-size: 14px;
padding: 0px;
margin-left: 10px;
border-radius: 5px;
color: var(--theme-palette-color-3);
cursor: pointer;
}
.content_toc_tree li.active,
.content_toc_tree li:hover {
color: var(--theme-palette-color-8);
background-color: var(--theme-palette-color-1);
}
.content_toc_tree li::before {
content: '';
position: absolute;
top: 12px;
left: -10px;
width: 5px;
height: 5px;
background-color: var(--theme-palette-color-6);
border-radius: 50%;
}
.content_toc_tree li.active::before,
.content_toc_tree li:hover::before {
border-width: 2px;
background: var(--theme-palette-color-1);
}
.content_toc_tree li.level_H1 {
padding-left: 5px;
}
.content_toc_tree li.level_H2 {
padding-left: 5px;
}
.content_toc_tree li.level_H3 {
padding-left: 1em;
}
.content_toc_tree li.level_H4 {
padding-left: 2em;
}
.content_toc_tree li.level_H5 {
padding-left: 3em;
}
.content_toc_tree li.level_H6 {
padding-left: 4em;
}
@media screen and (max-width: 992px) {
.content_toc {
width: 170px;
}
.content_toc_tree {
padding: 0 5px 10px 5px;
}
.content_toc_tree li {
width: 150px;
}
}
JS代码
为方便个性化定制,代码中增加了配置项。
/**
* 前端文章目录 - js
*
* @author 阿锋
* @link https://feng.pub
* @version 0.0.3
*/
document.addEventListener("DOMContentLoaded", () => {
// 配置项
// 获取内容的标题级别
const levelTOC = 'h1, h2, h3, h4'
// 获取到标题个数大于该数时才显示文章目录
const toShowNum = 3
// 文章目录块的高度偏移量(可视窗口高度减去该高度为文章目录块的高度)
const tocOffsetHeight = 235
// 滚动顶部偏移(滚动窗口时内容标题顶部偏移高度,防止顶部浮动导航遮挡标题)
const topTOCOffsetHeight = 65
// 关闭文章菜单后,圆球的高度(根据css样式设定)
const lessHeight = 40
// 菜单默认状态(1:打开状态;0:关闭状态)
const defaultState = 1
// 移动端菜单默认状态
const mobileDefaultState = 0
// 当前状态
const initTOCState = (document.documentElement.clientWidth <= 992) ? mobileDefaultState : defaultState
// 获取文章内容元素
const entryContent = document.querySelector('.entry-content')
// 获取元素中H1、H2、H3、H4、H5、H6
const contentHeadings = entryContent.querySelectorAll(levelTOC)
if (contentHeadings.length >= toShowNum) {
// 文章目录HTML结构
// TOC
const contentTOCDiv = document.createElement('div')
if (initTOCState === 1) {
contentTOCDiv.className = 'content_toc'
} else {
contentTOCDiv.classList = 'content_toc less'
}
// TOC title
const tocTitleDiv = document.createElement('div')
tocTitleDiv.className = 'content_toc_title'
contentTOCDiv.appendChild(tocTitleDiv)
// TOC title h6
const tocTitle = document.createElement('h6')
tocTitle.innerText = '文档目录'
tocTitleDiv.appendChild(tocTitle)
// TOC title btn
const contentTOCBtn = document.createElement('div')
if (initTOCState === 1) {
contentTOCBtn.className = 'btn'
contentTOCBtn.innerText = '×'
contentTOCBtn.setAttribute('title', '关闭文档目录')
} else {
contentTOCBtn.classList = 'btn close'
contentTOCBtn.innerText = '目录'
contentTOCBtn.setAttribute('title', '打开文档目录')
}
tocTitleDiv.appendChild(contentTOCBtn)
// TOC main
const contentTOCTreeDiv = document.createElement('div')
contentTOCTreeDiv.className = 'content_toc_main'
contentTOCDiv.appendChild(contentTOCTreeDiv)
// TOC main tree
const contentTOCTree = document.createElement('ul')
contentTOCTree.className = 'content_toc_tree'
contentHeadings.forEach((e, k) => {
// TOC main tree li
const toc = document.createElement('li')
toc.className = 'level_' + e.tagName
toc.innerText = e.textContent
toc.dataset.toc = k
contentTOCTree.appendChild(toc)
});
contentTOCTreeDiv.appendChild(contentTOCTree)
// 追加到body
document.querySelector('body').appendChild(contentTOCDiv)
let currentState = initTOCState
// 是否需要重设高度
let needSetHeight = 0
const action = {
// 设置文档目录的高度
setTOCHeight: () => {
// 标题块高度
const tocTitleHeight = tocTitleDiv.clientHeight
// 文档可视高度
const clientHeight = document.documentElement.clientHeight
// 文档目录树高度
const tocTreeHeight = contentTOCTree.clientHeight
// 文档目录块最大高度
const maxTOCHeight = clientHeight - tocOffsetHeight
// 文档目录块初始高度
const tocHeight = tocTreeHeight + tocTitleHeight > maxTOCHeight ? maxTOCHeight : tocTreeHeight + tocTitleHeight
// 文档目录块高度带单位px
const tocStyleHeight = tocHeight + 'px'
contentTOCDiv.dataset.height = tocStyleHeight
contentTOCDiv.style.height = tocStyleHeight
// 设置文档目录树块的高度
const tocMainHeight = tocHeight - tocTitleHeight
contentTOCTreeDiv.dataset.height = tocMainHeight + 'px'
contentTOCTreeDiv.style.height = tocMainHeight + 'px'
},
// 文档目录打开、关闭切换
toggleTOC: (to) => {
if (currentState === 0) {
// 当前是关闭状态
if (to == 'open' || to == undefined) {
contentTOCBtn.innerText = '×'
contentTOCBtn.setAttribute('title', '关闭文章目录')
contentTOCDiv.style.height = contentTOCDiv.dataset.height
contentTOCDiv.classList.toggle('less')
contentTOCBtn.classList.toggle('close')
// 更改状态
currentState = 1
if (needSetHeight === 1) action.setTOCHeight()
}
} else {
// 当前是打开状态
if (to == 'close' || to == undefined) {
contentTOCBtn.innerText = '目录'
contentTOCBtn.setAttribute('title', '打开文章目录')
contentTOCDiv.style.height = lessHeight + 'px'
contentTOCDiv.classList.toggle('less')
contentTOCBtn.classList.toggle('close')
// 更改状态
currentState = 0
}
}
}
}
// 默认打开状态时需设置高度
if (initTOCState === 1) {
action.setTOCHeight()
} else {
// 默认关闭状态需要重设高度
needSetHeight = 1
}
// 监听窗口变化需要重新计算高度
window.addEventListener('resize', () => {
if (currentState === 1) {
// 当前是打开状态,直接重设高度
action.setTOCHeight()
} else {
// 记录当前窗口已发生变化,需要重设高度
needSetHeight = 1
}
})
// 绑定打开/关闭目录事件
contentTOCBtn.addEventListener('click', e => {
action.toggleTOC()
})
// 点击文章目录事件
contentTOCTree.addEventListener('click', e => {
if (e.target.tagName == 'LI') {
const activeToc = contentTOCTree.querySelector('.active')
if (activeToc) activeToc.classList.remove('active')
e.target.classList.add('active')
const TOCIndex = e.target.dataset.toc
window.scrollTo({ top: contentHeadings[TOCIndex].offsetTop - topTOCOffsetHeight, left: 0, behavior: "smooth" })
}
})
// 监听页面滚动
document.addEventListener('scroll', () => {
const scrollTop = document.documentElement.scrollTop
const activeToc = contentTOCTree.querySelector('.active')
if (scrollTop < entryContent.offsetTop || scrollTop > (entryContent.offsetHeight + entryContent.offsetTop)) {
if (activeToc) activeToc.classList.remove('active')
} else {
const contentTOCLis = contentTOCTree.querySelectorAll('li')
contentHeadings.forEach((e, k) => {
if (scrollTop >= (e.offsetTop - topTOCOffsetHeight) && scrollTop < (contentHeadings[k + 1] ? contentHeadings[k + 1].offsetTop - topTOCOffsetHeight : entryContent.offsetHeight + entryContent.offsetTop)) {
if (activeToc) activeToc.classList.remove('active')
contentTOCLis[k].classList.add('active')
}
})
}
})
}
});
使用方法
将CSS代码及JS代码复制后添加到主题模板相应的css、js文件内即可。
某些程序或主题可通过后台添加css、js代码,直接在后台添加也可以。
挺好的,一直想加个目录
建议默认折起来,不是很习惯目录打开状态
因人而异,要改JS才能实现,忘记预留配置项了
昨天有博友说让我搞个返回顶部,就想到目录一起搞了,今天来你这就发现了,现成的用起来
^_^
有些主题都是集成了吧
是啊,国内大部分主题自带文章目录
折腾更健康
折腾着玩
我的博客目录也是用的wpjam的相关代码,但是没有你这么美观,我想让它和你博客一样,悬浮在侧边,不知是否有空帮忙看看?
之前用的WPJAM文章目录的JS、CSS代码整理好了,https://feng.pub/1020235787.html
有问题再沟通。
目录图标点击展开的时候滚动条看着有点碍事。
是有点难看,没有设置滚动条的样式。
改进隐藏滚动条