功能介绍
将帖子内部的标题提取出来, 按照层级顺序将他们呈现在目录上, 方便用户浏览大致内容
随着用户滑动界面, 目录会显示用户浏览的位置(具体表现为对应的标题演示变化, 加上active)
当用户直接点击目录上的某一个标题时, 界面也会跳转到相应的位置
学习: 对于任何浏览器, 怎么在不影响滚动功能的情况下, 隐藏滚动条
详解
样式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <div class="toc-panel"> <div class="toc-container"> <div class="toc-title">目录</div> <div class="hiddenMouse"> <div class="toc-list"> <template v-if="tocArray.length === 0"> <div class="no-toc">未解析到目录</div> </template> <template v-else> <div v-for="toc in tocArray"> <span class="toc-item" :class="{'active': toc.id === activeChorId}" :style="{'padding-left': toc.level * 10 + 'px'}" @click="gotoAnchor(toc.id)" >{{ toc.title }}</span> </div> </template> </div> </div> </div> </div>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| .toc-panel { position: absolute; top: 60px; right: 0px; width: 285px; .toc-container { position: fixed; margin: 5px 5px; width: 285px; background-color: #fff; .toc-title { border-bottom: 1px solid #ddd; padding: 10px; } .hiddenMouse { /*隐藏滚动条但不影响滚动效果*/ max-height: calc(100% - 30px); overflow: hidden; width: 285px; .toc-list { width: 290px; padding: 8px 0px 5px 10px; max-height: 500px; overflow: auto; .no-toc { text-align: center; color: #757474; line-height: 13px; font-size: 13px; } .toc-item { margin: 3px 0; cursor: pointer; display: block; line-height: 35px; overflow: hidden; text-overflow: ellipsis; color: #8d8c8c; font-size: 14px; border-radius: 3px; border-left: 2px solid #fff; } .toc-item:hover { color: #3f3f3f; border-radius: 0px; font-size: 15px; background-color: #f0eeee; border-left: 2px solid #8fc7d3; } .active { color: #3f3f3f; border-radius: 0px; font-size: 15px; background-color: #f0eeee; border-left: 2px solid #8fc7d3; } } } } }
|
代码实现
隐藏滚动条
类似于上面的这段代码
1 2 3 4 5 6 7 8 9 10
| .hiddenMouse { /*隐藏滚动条但不影响滚动效果*/ max-height: calc(100% - 30px); overflow: hidden; width: 285px; .toc-list { width: 290px; padding: 8px 0px 5px 10px; max-height: 500px; overflow: auto; }
|
给这个盒子再套一层, 设置最大高度和overflow:hidden属性
重点: 里面的这个盒子宽度必须略小于外面这个盒子的高度, 使滚动条能够超出父盒子的宽度限制, 达到隐藏效果
获取dom节点
遍历文章内容区域的所有子元素,找出标题标签(H1 到 H6),然后给这些标题添加唯一的 ID,并将标题信息(标题文本、层级、位置)存储在一个响应式数组中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| //更新目录列表 const tocArray = ref([]) const makeToc = () => { nextTick(() => { const tocTags = ["H1", "H2", "H3", "H4", "H5", "H6"]
//获取所有标签 const contentDom = document.querySelector('#detail') const childNodes = contentDom.childNodes let index = 0 //过滤非标题节点 childNodes.forEach((item) => { let tagName = item.tagName // console.log(tagName) if (tagName === undefined || !tocTags.includes(tagName.toUpperCase())) { return true //相当于continue } index++ let id = "toc" + index //生成唯一ID, 如"toc1" item.setAttribute("id", id) //修改属性 tocArray.value.push({ id: id, title: item.innerText, level: Number.parseInt(tagName.substring(1)), offsetTop: item.offsetTop, }) }) }) }
|
跳转到相应的锚点
1 2 3 4 5 6 7 8 9 10
| //跳转到相应的锚点 const activeChorId = ref(null) const gotoAnchor = (domId) => { activeChorId.value = domId const dom = document.querySelector('#'+domId) dom.scrollIntoView({ behavior: "smooth", //平滑滚动 block: "start", //把目标标题滚动到浏览器窗口的最上方 }) }
|
获取滚动条位置
1 2 3 4 5 6 7
| const getScrollTop = () => { let scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop return scrollTop }
|
同步当前滚动条的位置与目录中活跃的标题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| const listenerScroll = () => {
let currentScrollTop = getScrollTop() //获取当前的位置 //.some() 方法会遍历数组中的每一个元素,并对每个元素执行传入的回调函数 //不会修改原数组 tocArray.value.some((item, index) => { if (index < tocArray.value.length - 1 && currentScrollTop >= tocArray.value[index].offsetTop && currentScrollTop < tocArray.value[index + 1].offsetTop || index == tocArray.value.length - 1 && currentScrollTop < tocArray.value[index].offsetTop ) { activeChorId.value = item.id //同步样式状态 return true } }) }
|
.some
挂载与清空
1 2 3 4 5 6 7
| onMounted(() => { window.addEventListener("scroll", listenerScroll, false) })
onUnmounted(() => { window.removeEventListener("scroll", listenerScroll, false) })
|