【vue】【intersectionObserver】滚动正文时菜单滚动到对应菜单

本文介绍如何在Vue项目中利用intersectionObserver监听元素是否在可视区域,结合scrollIntoView实现菜单滚动到对应内容,以及内容滚动时菜单切换。详细阐述了思路、HTML、CSS和JS实现,并探讨了getBoundingClientRect和Object.entries等附加知识点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

场景需求:

  • 左侧为菜单列表,右侧是对应菜单的内容,
  • 所有内容可进行无缝滚动
  • 当内容滚动到对应的菜单时,菜单进行定位并显示到可视区域
  • 点击菜单时,对应内容显示到可视区域

涉及知识点:

思路

切换菜单时,内容滚动到对应菜单
  • 定义变量active存储当前菜单名称,获取当前内容ref进行scrollTop滚动
效果图

在这里插入图片描述

滚动内容时,菜单切换对应菜单并可见
  • 页面加载时使用intersectionObserver.observe()建立观察,观察每一个内容是否在可视区域
  • 当内容出现在可视区域时,当entrie.intersectionRatio返回值不是0的时候,使用entrie.target.innerHTML.trim()拿到当前激活的菜单名称赋值给active
  • 此时active的值进行变化,有了新的ref名称activeMenu,于是使用scrollIntoView将新的激活菜单滚动到可视区域即可
效果图

在这里插入图片描述

HTML

  <div class="content-box">
    <div class="menu-tabs">
      <div
        class="tab"
        :class="{'tab-active':active === item.name}"
        v-for="(item,index) in menuList"
        :key="index"
        :ref="active === item.name ? 'activeMenu':''"
        @click="menuClick(item.name,index)">
        <span>{{ item.name }}</span>
      </div>
    </div>
    <div class="content-wrap" id="contentWrap">
      <div
        class="content"
        v-for="(content,index) in this.contentList"
        :key="index">
        <div class="active-title" ref="title"> {{ content.name }}</div>
        <div class="content-html" v-html="content.content"/>
      </div>
    </div>
  </div>

CSS

.content-box{
  display: flex;
  height: 80%;
  background-color: #fff;
  .menu-tabs{
    width: 210px;
    font-size: 24px;
    overflow-x: hidden;
    .tab{
      @include fw500;
      width: 206px;
      height: 80px;
      display: flex;
      align-items: center;
      margin-bottom: 10px;
      padding-left: 20px;
      background-repeat: no-repeat;
      background-image: url('~@/assets/images/disease/tab-bg.png');
      background-size: cover;
      line-height: 3.5;
      &.tab-active{
        background-image: url('~@/assets/images/disease/tab-bg-active.png');
      }
    }
  }
  .content-wrap{
    flex: 1;
    font-size: 40px;
    padding: 0 16px;
    overflow-y: scroll;
    .content-html{
      font-size: 30px;
      text-indent: 2em;
      line-height:54px;
      font-weight: 400
    }
  }
}

JS

 data(){
    return {
      active: '第1集',
      menuList:[{ name:'第1集' },{ name:'第2集' },{ name:'第3集' },{ name:'第4集' },{ name:'第5集' },{ name:'第6集' },{ name:'第7集' },{ name:'第8集' }],
      contentList:[
        { name:'第1集',content:'江湖上传言,二十年前,大魔头容炫在青崖山被五湖盟及天下英雄伏诛,他临终前留下了能让人一夜之间无敌于天下的武库,如果想打开.' },
        { name:'第2集',content:'温客行一眼就看出周子舒使用的是四季山庄的流云九宫步,狠狠教训了顾湘一顿,就带她离开了。张成岭看出周子舒有一身好武功,只是深藏不露,就主动过来和周子舒寒暄,还给他一块名帖,让他有事去镜湖山庄,张成岭着急给母亲买点心,就先行离开了。周子舒听到孩子们在唱那首五湖盟争夺武林盟主以及琉璃甲的歌谣,不禁感慨江湖的风云多变。周子舒叫醒岸边的摆渡船夫,他要乘船去镜湖山庄,摆渡船夫趁机狮子大开口,周子舒也不还价,摆渡船夫看他一副病恹恹的模样,不忍心敲诈他,温客行带顾湘及时赶来,主动提出送周子舒去镜湖山庄,摆渡船夫不依不饶,拉起周子舒就上船离开了。周子舒远远就发现镜湖山庄犹如人间仙境,他迫不及待赶过去,下船就忘了付钱,遭到摆渡船夫劈头盖脸一顿臭骂,周子舒索性就坐一次霸王船。周子舒施展轻功,很快就进入镜湖山庄的桃林,他沉醉于花香之中,温客行突然从背后偷袭,周子舒只能迎战,两个人交手几个回合,温客行对周子舒心生佩服.' },
        { name:'第3集',content:'温客行认定周子舒易了容,几次三番想揭穿他的真面目,周子舒巧妙应付过去,温客行好奇镜湖山庄为何会招惹青崖山鬼谷的人,张成岭也毫不知情,周子舒更不感兴趣,温客行搬出孩童们的歌谣来说事,口口声声称江湖上盛传大魔头容炫留下能让人无敌于天下的武库,打开武库需要琉璃甲,各大门派为了得到琉璃甲互相残杀,周子舒觉得这些人就是想不劳而获,不值得浪费时间讨论,温客行也只好闭嘴。大孤山派掌门沈慎架着一叶小舟来到镜湖山庄,发现这里横尸遍野,岳阳派首徒邓宽等人在帮忙清理尸体,沈慎得知结拜兄弟张玉森,张成峦和张成峰被人虐凌而死,他伤心地痛不欲生,邓宽跪倒在地向沈慎认错,他们来给张玉森送请柬,没想到码头的船都被人赶走了,他们眼看着镜湖山庄着火,最后赶到的时候为时已晚,沈慎忍不住仰天长叹,发誓要为张玉森父子三人报仇。桃红婆和绿柳翁随后赶来,对沈慎恶语相向,大骂他是五湖盟盟主高崇的走狗,让他也追随张玉森一起去死,沈慎气得咬牙切齿,丐帮长老黄鹤及时赶来劝解,劝他们以大局为重,暂时放下个人恩怨。' },
        { name:'第4集',content:'周子舒带着张成岭离开客栈,温客行在路口已经等候多时,张成岭感谢他的救命之恩,就在这时,丐帮大智分舵副舵主跛脚乞丐喊住张成岭,他自称受了黄鹤的委托寻找张玉森的儿子张成岭,丐帮弟子闻讯都围拢过来,张成岭不认识他们,吓得躲到周子舒身后,跛脚乞丐口口声声称沈慎委托丐帮帮忙,坚持要把张成岭带走,张成岭坚决不跟他们走,跛脚乞丐一口咬定周子舒给张成岭下了药,温客行当场和跛脚乞丐发生争执。跛脚乞丐一声令下,丐帮弟子摆出密不透风的大阵,要围攻周子舒,周子舒拜托温客行照顾张成岭,他上下翻飞,不费吹灰之力就把丐帮的大阵攻破,张成岭不禁替周子舒捏了一把汗,让温客行去帮忙,温客行坚信周子舒一个人就能应付。周子舒随后拎起地上的一袋子黄豆洒在地上,丐帮弟子们被摔得东倒西歪,跛脚乞丐趁机去抓张成岭,周子舒被团团包围,温客行催周子舒亮出兵器,周子舒对他置之不理,想突出重围去救张成岭,突然口吐鲜血,温客行把张成岭救下来,周子舒拉起张成岭飞走了,温客行掐死跛脚乞丐,把丐帮弟子们也一一打死。周子舒带张成岭来到小胡同里,他体力渐渐不支,只好原地打坐修复元气。' },
        { name:'第5集',content:'沈慎把桃红婆和绿柳翁打跑,让傲崃子把陆太冲的两个弟子交出来,傲崃子坚决不干,弟子们也想跟着傲崃子,沈慎恼羞成怒,打着保护丹阳派的旗号相威胁,傲崃子毫不畏惧,两个人一言不合就剑拔弩张。青华带着赵敬等人及时赶来制止,沈慎和赵敬称兄道弟,详细讲述了他打跑桃红绿柳的经过,赵敬请傲崃子带着丹阳派的两位弟子去三白山庄,傲崃子断然拒绝,赵敬想把丹阳派的两位弟子留下,他们要谨遵师命跟着傲崃子,赵敬也不再勉强,就此和傲崃子一行人告别。赵敬向沈慎隆重介绍了周子舒和温客行,沈慎看到张成岭还活着,对他们俩表示感谢。赵敬设宴请周子舒和温客行,请来五湖盟的各门派作陪,还让歌姬伴舞助兴,温客行和他们推杯换盏。傲崃子带着众弟子一口气跑到断剑山庄的地盘,眼看天色已晚,突然看到断剑山庄的少庄主穆云歌一路狂奔,嘴里喊着救命。穆云歌向傲崃子求救,口口声声称有女鬼追他,空中传来女鬼幽怨的骂声,大骂穆云歌是薄情郎,没等傲崃子醒过味来,穆云歌就被女鬼抓走了。沈慎借着酒劲不停地劝张成岭喝酒,张成岭不会喝,赵敬赶忙过来解围,让岳阳派弟子宋怀仁把沈慎搀回去休息,派人把张成岭送回房间。' },
        { name:'第6集',content:'温客行突然发现其中一个棺材板剧烈晃动,没等周子舒反应过来,从棺材里跳出来十大恶鬼之一的吊死鬼,他自称给周子舒和温客行下了迷香,摇铃铛把其他棺材里的人都喊出来,每个棺材里都跳出来一个铜皮铁臂的药人,他们团团围住周子舒,对他痛下杀手,周子舒深知药人浑身都是毒药,他无力反抗,可又不甘心就这样死去,多亏温客行及时出手,把那些药人全部打死,周子舒从吊死鬼手里抢了一个缠魂丝匣。周子舒手臂被药人划伤,他先给温客行一颗解药,又用匕首把伤口划开,把里面的毒液吸出来,温客行发现他肩膀上也有伤口,而且身上其他地方还有内伤,赶忙用嘴帮周子舒把肩膀的毒吸出来,周子舒极力掩饰身体有内伤,温客行当面揭穿他用了易容术,好奇他想躲着什么仇家,答应全力以赴帮他对付仇家,劝周子舒和他坦诚相待,周子舒断然拒绝,他不想让任何人看到自己真实的样子。温客行出手试探周子舒的武功,两个人你来我往,互不相让,周子舒因内伤输了一招,他失足跌落水中,温客行见他迟迟不出来,赶忙跳进水池去救他,周子舒撕开面具露出真容,两个人一起上岸烤火,温客行目不转睛盯着周子舒俊秀的面庞,证实了自己的判断,两个人不打不相识.' },
        { name:'第7集',content:'鬼谷的喜丧鬼号称薄情簿主,扬言要杀尽天下所有的负心汉,喜丧鬼听说穆云歌对峨眉弟子莫燕婉始乱终弃,导致莫艳婉含恨吊死在断剑山庄.。' },
        { name:'第8集',content:'高崇对张成岭嘘寒问暖,迫不及待向他讨要琉璃甲,张成岭一问三不知,赵敬赶忙为张成岭解围,谎称他暂时失忆,劝高崇给他一点时间,高崇坚决不干,对张成岭苦苦相逼,沈慎也在一旁帮腔,张成岭大为恼火,他反复讲明不知道琉璃甲的下落,高崇根本不信,还对他威胁恐吓一番。赵敬赶忙从中劝解,高崇更是气不打一处来,把他对张玉森的恨全撒在张成岭身上,赵敬好说歹说才把他劝走,沈慎也对赵敬大为不满,埋怨他太迁就张成岭。曹蔚宁荷包被偷无法结账,赶忙过来向周子舒和温客行赔罪,周子舒邀请他坐下来一起喝酒,他们俩一见如故,把酒言欢,温客行看曹蔚宁不顺眼,示意顾湘把他撵走,顾湘想利用曹蔚宁的关系混进岳阳派,温客行不依不饶,逼顾湘去把偷荷包的贼方不知抓来,曹蔚宁深知方不知的厉害,主动提出帮顾湘去找。曹蔚宁对美食很有研究,他想请顾湘品尝当地美食,顾湘自然求之不得。周子舒早就听说清风剑派的掌门是一只老狐狸,没想到他竟然曹蔚宁这样单纯的儿子,温客行看到周子舒对曹蔚宁热情态度就生气,忍不住对周子舒冷嘲热讽,周子舒只是想通过他打听一下张成岭的下落,没想到温客行已经派顾湘跟着曹蔚宁去岳阳派,设法打听张成岭的消息。周子舒却不买账。' },
      ],
      intersectionObserver:null, // 是否元素在可是区域
    }
  },
  created() {
    this.$nextTick(()=>{
      this.initIntersectionObserver()
    })
  },
  methods: {
    // 切换菜单
    menuClick(item,index){
      this.intersectionObserver.disconnect()
      this.active = item
      let contentWrapEl = document.getElementById('contentWrap')
      let activeEl = this.$refs['title'][index]
      contentWrapEl.scrollTop = activeEl.offsetTop - activeEl.offsetHeight - 70
    },
    // 当文档进入可视区域时进行跳转tab操作
    initIntersectionObserver(){
      this.intersectionObserver = new IntersectionObserver((entries) => {
        let entrie = entries[0];
        if(!entrie.intersectionRatio)return false;
        this.active = entrie.target.innerHTML.trim();
        setTimeout(()=>{
          this.$refs['activeMenu'][0].scrollIntoView();
        })
      })
      this.$refs['title'].forEach(ele=>{
        this.intersectionObserver.observe(ele)
      })
    },
  },

附加知识点:

Element.getBoundingClientRect() 方法返回元素的大小及其相对于视口的位置。

在这里插入图片描述

Object.entries()方法返回一个给定对象自身可枚举属性的键值对数组,其排列与使用 for…in 循环遍历该对象时返回的顺序一致(区别在于 for-in 循环还会枚举原型链中的属性)。

在这里插入图片描述
总结:实现这个功能时,最开始我用的是swipe+getBoundingClientRec的方式实现,后面是另一个前端小伙伴提醒说可以使用intersectionObserver,于是才换成现有的方式。无论和哪种人共事,只要他身上有可取之处,就是值得你学习的,加油。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值