这是我参与「第五届青训营 」伴学笔记创作活动的第 14 天
目录点击时样式
- 点击对应标题时,通过点击事件传递当前的标题index,通过定义一个 activeIndex 来判断是哪个导航条高亮样式。
- 这里通过获取的h标签等级来进行导航条的缩进 :style="{ marginLeft: `${hItem.id * 10}px` }
<li v-for="(hItem, index) in hList" :key="index" :title="hItem.content"
:class="[hItem.id * 1 === 1 ? 'a-container-h1' : 'a-container-h2', activeIndex === index ? 'active' : '']">
<div class="a-container" :style="{ marginLeft: `${hItem.id * 10}px` }">
<a :href="hItem.jumpId" @click="jump(index)">{{ hItem.content }}</a>
</div>
</li>
let activeIndex = ref(0);
const jump = (index) => {
activeIndex.value = index;
};
导航高亮样式:
.active {
a {
color: var(--theme-dir-active);
}
& ::before {
content: '';
position: absolute;
top: 2px;
left: 0;
margin-top: 7px;
width: 4px;
height: 16px;
background: var(--theme-dir-active);
border-radius: 0 4px 4px 0;
}
}
目录滚动高亮优化
在页面挂载完成后,获取所有需要的h标签距离顶部的距离存入 hTopList 数组。
bug发现!
如果文章中存在图片,图片还未加载完就获得了hTopList,导致目录点击转跳跟随错位,我明明使用的a标签转跳,img的src加载...。虽然我添加了延时,本地是解决了但是放到服务器上不行,所以本项目中没让数据人员给我在文章中添加图片数据。
我还尝试在文章图片加载完后,再获取hTopList,但也失败了,我觉得这和markdown css里定义的img样式有关。我应该去解决一下。
//获取所有h标签的offsetTop
let hTopList = ref([]);
const getHTopList = () => {
let arr = [];
for (let i = 0; i < hList.length; i++) {
arr.push(document.querySelector(hList[i].jumpId).offsetTop);
}
arr.push(Number.MAX_VALUE); //兜底
hTopList.value = arr;
};
目录跟随文章滚动
监听页面滚动,对比页面滚动值scrollTop和标签的高度来判断是否高亮,
for (let i = 0; i < hTopList.value.length; i++) {
if (scrollTop.value > hTopList.value[i] && scrollTop.value < hTopList.value[i + 1] && activeIndex.value != i) {
activeIndex.value = i;
}
}
滚动时样式优化
这里通过滚动目录div来实现。这里的keep参数就是导航条过多时,滚动条高亮处在第七个,其实应该根据导航box的高度对半取整算一下,这里图方便,数了下掘金是在第几个高亮就写了第几个。34 是每条导航 li 标签的高度。
let catalogList = document.querySelector('.catalog-list');
const keep = 7;
let hListLength = hList.length;
if (activeIndex.value <= keep) {
catalogList.scrollTop = 0;
} else if (activeIndex.value > hListLength - keep) {
catalogList.scrollTop = 34 * (hListLength - keep);
} else {
catalogList.scrollTop = 34 * (activeIndex.value - keep);
}
函数节流
在实现文章目录滚动样式,使用到了 window.addEventListener 监听鼠标滚动。每一次滚动都会调用一次内部函数。这里采用节流,每0.1秒(草)内行数只执行一次。
let boxTop = ref(0);
let scrollTop = ref(0);
let scroll;
let timer;
onMounted(() => {
setTimeout(() => {
boxTop.value = document.querySelector('.catalog-box').offsetTop;
}, 0);
setTimeout(() => {
getHTopList();
}, 2000);
getHTopList();
window.addEventListener(
'scroll',
(scroll = () => {
if (timer) {
return;
}
timer = setTimeout(() => {
scrollTop.value = window.scrollY || window.pageYOffset || document.documentElement.scrollTop;
console.log(scrollTop.value);
handleScroll();
watchActive();
timer = null;
}, 100);
}),
);
});