vue.js自动提取标题生成右侧目录(实现监听滚动,锚点定位)
开开 2021-06-10 18:22:37 2021-06-10 216 0
效果图如下:
代码:<template> <div class="icon-sources-wapper wapper"> <el-breadcrumb class="icondetials_header" separator-class="el-icon-arrow-right"> <el-breadcrumb-item :to="{ path: '/resources' }">组件库</el-breadcrumb-item> <el-breadcrumb-item>简介</el-breadcrumb-item> </el-breadcrumb> <el-main> <h1 id="-">一级目录</h1> <br> <br> <br> <br> <h2 id="-1">二级目录1</h2> <br> <br> <br> <br> <h3 id="-1">三级目录1</h3> <br> <br> <br> <br> <h1 id="-">一级目录</h1> <br> <br> <br> <br> <h4 id="-1">四级目录1</h4> <p>内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容</p> <p><img src="/Users/chenyujun/Desktop/截屏2020-07-14 下午1.56.11.png" alt="截屏2020-07-14 下午1.56.11"></p> <h4 id="-2">四级目录2</h4> <p>内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容</p> <h3 id="-2">三级目录2</h3> <p>内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容</p> <h2 id="-2">二级目录2</h2> <p>内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容</p> <h4 id="-4">四级目录4</h4> <p>内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容</p> <h4 id="-4">四级目录4</h4> <p>内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容</p> <h4 id="-4">四级目录4</h4> <p>内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容</p> <h5 id="-5">五级目录</h5> <p>内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容内容</p> </el-main> <el-aside> <div style="margin:0px 0 0 180px;font-size:18px;font-weight:bold;">目录</div> <el-tabs @tab-click="handleClick" v-model="activeName" :tab-position="tabPosition" style="height: auto;"> <el-tab-pane :name="'tab'+index" :class="item.lev" v-for="(item, index) in navList" :key="index" :label="item.name"></el-tab-pane> </el-tabs> </el-aside> </div></template><script>export default { data(){ return{ activeName:'tab0', tabPosition:'right', scroll: '', navList: [], } }, methods: { handleClick(tab,event){ this.jump(tab.index) }, dataScroll: function () { this.scroll = document.documentElement.scrollTop || document.body.scrollTop; }, jump(index) { let jump = document.querySelectorAll("h1,h2,h3,h4,h5,h6"); // 获取需要滚动的距离 let total = jump[index].offsetTop - 80; // Chrome document.body.scrollTop = total; // Firefox document.documentElement.scrollTop = total; // Safari window.pageYOffset = total; // $('html, body').animate({ // 'scrollTop': total // }, 400); }, loadScroll: function () { let self = this; let navs = document.querySelectorAll('.el-tabs__item'); // var sections = document.getElementsByClassName('section'); for (var i = self.navList.length - 1; i >= 0; i--) { if (self.scroll >= self.navList[i].offsetTop - 120) { self.activeName = 'tab'+i break; } } }, selectAllTitle(){ let title = document.querySelectorAll("h1,h2,h3,h4,h5,h6"); this.navList = Array.from(title); this.navList.forEach(item=>{ item.name = item.innerHTML }) this.navList.forEach(el => { let index = el.localName.indexOf('h'); el.lev = 'lev'+el.localName.substring(index+1,el.localName.length) }); } }, watch: { scroll: function () { this.loadScroll() } }, created(){ }, mounted() { // scroll代表滚动条距离页面顶部距离 window.addEventListener('scroll', this.dataScroll); this.selectAllTitle(); this.$nextTick(() => { setTimeout(() => { let navs = document.querySelectorAll('.el-tabs__item'); for(let i=navs.length-1;i>=0;i--){ // console.log($('#'+navs[i].id)) // 从lev1到lev5分别添加不同到样式 document.querySelector('#'+navs[i].id).style.padding="0"; if(this.navList[i].lev=='lev1'){ document.querySelector('#'+navs[i].id).style.paddingLeft="20px" ; }else if(this.navList[i].lev=='lev2'){ document.querySelector('#'+navs[i].id).style.paddingLeft="35px" ; }else if(this.navList[i].lev=='lev3'){ document.querySelector('#'+navs[i].id).style.paddingLeft="50px" ; }else if(this.navList[i].lev=='lev4'){ document.querySelector('#'+navs[i].id).style.paddingLeft="65px" ; document.querySelector('#'+navs[i].id).style.fontWeight="400" ; }else if(this.navList[i].lev=='lev5'){ document.querySelector('#'+navs[i].id).style.paddingLeft="80px" ; document.querySelector('#'+navs[i].id).style.fontWeight="400" ; } } }); }) }}</script><style lang="scss" > .el-main{ width:900px;}.el-tabs__header.is-right{ height: 500px !important;}.el-aside{ position: fixed; top: 108px; right: 160px; width: 220px; height: auto;}.icon-sources-wapper.wapper .el-tabs__nav.is-right{ box-sizing: content-box !important; }</style>
这里面使用的是elementui的组件tabs
1.首先要实现锚点定位
jump方法是用来获取各个节点在页面中距离顶部的位置的,handleClick()方法是elementui组件tabs预留的方法,用于点击切换tab页,将jump()方法放在handleClick()方法中就可以获取到tabs中所有标题节点,点击任意标题,就可以获取到该标题对应节点距离顶部的距离。document.body.scrollTop,document.documentElement.scrollTop,window.pageYOffset这几个是不同浏览器中滚动条距离顶部的距离属性,在获取到对应节点的位置后再将节点的位置设置到滚动条距离顶部的距离上就可以实现点击标题,页面自动滚动到该标题在页面中对应的位置上2.实现鼠标滚动目录获得焦点的标题也对应发生变化
这个效果主要通过监听watch来实现,监听滚动条的位置,一旦发生变化就会执行loadScroll()方法,在vue中:name=‘’tab‘+index’是给每个tabs分页都加上一个name属性,属性值为‘tab+元素对应的index值’,activeName是tabs组件预留的属性,activeName=name名,那么对应name名的元素就会显示为获得焦点样式,然后通过loadScroll()方法可以从遍历每个节点然后对比出符合位置的节点,将该节点的name值给activeName,这样目录就会显示对应的标题高亮,从而实现滚动条位置改变,目录高亮标题发生对应变化3.更改目录标题的字体和排版样式
我这里用selectAllTitle()方法给每个节点加上了一个lev属性,例如:h1标签,lev属性值就为lev1,h2标签,lev属性值就为lev2。可以看见每个tabs分页的class属性中都有el-tabs__item但是直接在mounted中是获取不到的,所以要在👇这里面获取,这个elementui组件节点挂载的问题this.$nextTick(() => {
setTimeout(() => {}
}然后就是给lev1-lev5写样式了,写完就搞定了这有一个要注意的问题:不能在elementui的组件中用:class=‘class名’这样的方法给elementui的组件添加类名,就算成功添加上类名,tabs每次更改获得焦点的分页新加上的类名都会消失,我开始也想给每个tab加上lev1-lev5的类名,然后直接在scss里写样式,但是发现每次切换分页,写好的样式就消失了。所以要么自己重写elementui中tabs组件的代码,要么就按我上面的方法
以上是自己在开发过程中想到的一种方法,肯定还有其他方法可以实现,只是我刚学不久,水平不够实在看不懂那些厉害的方法。之后如果想到更好的办法会再分享。
原文: https://blog.csdn.net/weixin_46363283/article/details/107635642