文章目录
- 锚点定位功能总述
- 提取标签内的关键词
- 目录的渲染和点击跳转
- 1.目录渲染
- 2.锚点跳转
- 总结
锚点定位功能总述
在系统中会有管理端和客户端两种,在管理端中通过wangediter发布文章,然后在客户端中获取文章的数据,将其中h1和h2标签中的文字提取出来,然后渲染到右边导航栏中,然后再绑定点击事件,通过js去写跳转到动画,然后实现锚点定位到功能。
提取标签内的关键词
就是把后端传过来的html的字符串先渲染到页面上,然后将字符串中的h1标签和h2标签进行内容和ID提取,并将h2标签放入到相应的h1标签的子数组中,形成一个数组对象,为下一步法定位标题的渲染作出准备。
// 从后台获取到的文章数据(wangediter中的数据)
async getGuideContent() {
const { respData } = await getGuide()
this.GuideContent = respData[0]
// 获取h1标签的所有内容
let ghead = document.getElementsByTagName('h1')
// 获取一级标题的所有内容
this.$nextTick(() => {
// 获取到的h1标签的个数
let hlength = ghead.length
for (let i = hlength - 1; i >= 0; i--) {
// 数组的每一项都是一个对象
this.GuideFixed[i] = {}
// h1标签的内容进行赋值
this.GuideFixed[i].name = ghead[i].innerText
// h1标签的id进行改变和赋值,wangediter中的id会是数字开头,所以需要进行处理
ghead[i].id = 'a' + ghead[i].id
this.GuideFixed[i].id = ghead[i].id
this.GuideFixed[i].list = []
// 筛选出此id位置下方的所有h2标签
this.GuideFixed[i].children = document.querySelectorAll(
`#${this.GuideFixed[i].id}~h2`
)
// 将数组中的多余数据进行处理赋值
let ghead2 = this.GuideFixed[i].children.length
// 这个位置就是会有些重复h1标签的提取过程,注意二次循环的问题
for (let j = 0; j < ghead2; j++) {
this.GuideFixed[i].list[j] = {}
this.GuideFixed[i].list[j].id = this.GuideFixed[i].children[j].id
this.GuideFixed[i].list[j].name =
this.GuideFixed[i].children[j].innerText
}
delete this.GuideFixed[i].children
}
// 去重结构
// 因为h1它下方的所有h2,所以需要减去下一个的重复的内容,直接用数组长度就可以看
for (let k = 0; k < ghead.length - 1; k++) {
let long1 = this.GuideFixed[k].list.length
let long2 = this.GuideFixed[k + 1].list.length
let long3 = long1 - long2
// 处理好的单条数据
this.GuideFixed[k].list = this.GuideFixed[k].list.slice(0, long3)
}
})
},
处理后的数据大致如下图所示
目录的渲染和点击跳转
1.目录渲染
由于数据处理已经在上面的步骤中完成,所以下面的步骤就是需要将目录渲染至界面,然后去实现跳转。
在渲染中我采用了ul和li来进行渲染。
其中需要注意的是,我在li中又套了ul,所以会把文字都放在span中,方便去绑定点击事件
<div class="af-category">
<div class="af-common-title">目录</div>
<div style="font-size: 14px">
<ul class="p-title-container">
<li
v-for="(item, i) in GuideFixed"
:key="i"
class="p-title"
:class="{
'title-selected': isSelect(i),
}"
>
<div class="line" v-show="isLineShow(i)"></div>
<div class="circle"></div>
<span class="p-text" @click="jump(item, i)">{{ item.name }}</span>
<ul>
<li
v-for="(children, j) in item.list"
:key="j"
class="c-title"
:class="{
'c-title-selected': isSelected(i, j),
}"
@click="jump(children, j)"
>
{{ children.name }}
</li>
</ul>
</li>
</ul>
</div>
</div>
这个位置就是需要注意双层循环和数据绑定就行了,别的地方感觉没有什么难点。
2.锚点跳转
感觉到这个位置才完全的切入正题,前面的所有铺垫都是为了在这个位置去实现锚点定位的跳转,由于在数据处理的时候获取了标签的id,就可以通过id去获取高度从而去进行计算滚动的高度了
// 跳转的相关内容
jump(item, index) {
// 获取该元素的高度
let scrolly = document.getElementById(item.id).offsetTop
// 获取当前页面滚动的高度
let scrollTop = document.documentElement.scrollTop
// 距离差减去导航栏的高度
let distance = scrolly - scrollTop - 105
let everyDistance = distance / 30
// 循环n次,每次跳转一点,n次之后到位
// 可以去实现平滑跳转
for (let i = 1; i <= 30; i++)
setTimeout(function () {
window.scrollBy(100, everyDistance)
}, 10 * i)
},
在此还需要对滚动条进行相关的操作,就是通过距离判断,是否触底等情况进行处理
// 滚动绑定的函数
onScroll(e) {
let scrollItems = document.querySelectorAll('.w-e-text')
for (let i = scrollItems.length - 1; i >= 0; i--) {
// 判断滚动条滚动距离是否大于当前滚动项可滚动距离
let judge =
e.target.scrollTop >=
scrollItems[i].offsetTop - scrollItems[0].offsetTop
if (judge) {
this.catalogCur = i
break
}
}
//滚动条触底了
if (
e.srcElement.scrollTop + e.srcElement.offsetHeight ==
e.srcElement.scrollHeight
) {
if (this.GuideFixed) {
this.catalogCur = this.GuideFixed.length - 1
} else {
this.catalogCur = this.quizFixed.length - 1
}
}
},
总结
我认为在我这个项目中锚点定位的相关功能的主要难点在于对数据的处理,还有就是跳转方法和高度的计算,当时也用过别的方法,但是兼容性方面不能够满足我的全部要求,所以后面选择用js去做跳转,但是js做跳转会比较吃电脑的分辨率,会出现一点点视觉的卡顿问题,dbq,虽然我看不出来,但是这也是我以后的优化方向吧,可以改成css动画去做,在这个功能开发中,主要是对于问题的解决思路和想法会拦住我开发的脚步,好在后面顺利完成,如果以后有相似的功能,我也可以通过此博客来进行复习和巩固。