【vue+a链接动态锚点】监听滚动事件,实现动态锚点。左侧菜单列表,右侧内容区,点击快速定位+滑动到对应内容区高亮对应菜单项

这个效果花了两天的时间,终于实现 ,可以点击快速定位,也可以滑动左侧对应高亮
在这里插入图片描述
主要用到a标签的动态锚点,scrollIntoView方法,监听元素的滚动事件

1、a标签的动态锚点

实现方式以我理解的如下:
通过a标签的href属性与其他元素(如div)的id相对应,给a链接加点击事件,让点击的id等于容器的id,实现“id”定位,因为用了div包裹a标签 并且for循环,所以好像不用写href属性,有key就行了。

因为项目里大量使用此功能,为了方便,所以把左侧菜单列表进行了封装,其实也可以都写在父组件中,不用慌,我会把思路都捋一遍

子组件
=-=-=在子组件里循环的rightBlocks,在父组件中是数组,用来装左侧菜单列表的id和name
=-=-= a标签的类名就是给选中项加高亮的样式
=-=-=a标签点击事件中赋值和使用scrollIntoView方法

<template>

  <div class="box-left-position" ref="boxPosition" >
    <div class="left-content">     
      <div  class="arrowBox" >
        <div v-for="(item, index) in rightBlocks" :key="index">
          <a :class="rightId === item.id ? 'arrow-a-border' : ''" @click.stop="scrollHome(item.id)">{{ item.name }}</a>
        </div>
      </div>
    </div>
  </div>
</template>
props: {
    // 导航的项
    enterOptions: {
      type: Array,
    },  
  },
 data() {
    return {
      //导航的数据
      rightBlocks: [],
      //右侧内容id
      rightId: 'a1',
    }
  },
   /** 点击快速定位 */
    scrollHome(id) {    
      this.rightId = id
      document.getElementById(id).scrollIntoView({
        block: 'start',
        behavior: 'instant',
      })
    },

父组件
给对应内容区的标题(头部容器)加id,我的布局不是这样,我只是把所有的头部都单独拿出来示范

    <!-- 左侧的导航组件 -->
        <LeftQuickEnter :enterOptions="rightBlocks" ref="leftQuick"/ >
    <!-- 右侧的内容区 -->     
 <div slot="header" class="clearfix cardTitle" id="a1">
 <span style="float: left">xxx标准</span>
  </div>
   <div slot="header" class="clearfix cardTitle" id="a2">
 <span style="float: left">xxx标准</span>
  </div>
   <div slot="header" class="clearfix cardTitle" id="a3">
 <span style="float: left">xxx标准</span>
  </div>
   <div slot="header" class="clearfix cardTitle" id="a4">
 <span style="float: left">xxx标准</span>
  </div>
   <div slot="header" class="clearfix cardTitle" id="a5">
 <span style="float: left">xxx标准</span>
  </div>
   <div slot="header" class="clearfix cardTitle" id="a6">
 <span style="float: left">xxx依据</span>
  </div>

在data中定义rightBlocks数组

// 左侧导航的数据
      rightBlocks: [
        { id: 'a1', name: 'xxx标准' },
        { id: 'a2', name: 'xxx标准' },
        { id: 'a3', name: 'xxx标准' },
        { id: 'a4', name: 'xxx标准' },
        { id: 'a5', name: 'xxx标准' },
        { id: 'a6', name: 'xxx依据' },
      ],

2、scrollIntoView方法

将元素滚动到浏览器窗口的可视区域内
在这里插入图片描述
主要用到以下的behavior和blcok属性
在这里插入图片描述
在子组件的点击事件里使用,过渡动画原先用的是smooth,这里有个坑,就是点击的时候不顺滑,会有抖动及闪现的问题出现,这是因为使用了原生的监听事件,也就是鼠标滚动事件和这个冲突了,改成instant之后就没有这个问题了
子组件

  /** 点击快速定位 */
    scrollHome(id) {    
      this.rightId = id
      document.getElementById(id).scrollIntoView({
        block: 'start',
        behavior: 'instant',
      })
    },

3、监听元素的滚动事件

这是比较重点的一块,正常是监听浏览器滚动事件,即
父组件

//在mounted中
window.addEventListener('scroll', this.scrollList, true);  // 监听(绑定)滚动条滚动事件

这里也有坑,花了我半天时间排查,我绑了事件后,在对应的方法里获取滚动条被卷去的高度,即
父组件

var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;

我怎么打印scrollTop 的结果都是0,然后我又去百度为什么结果是0,网上的解释大致是页面是否具有DTD(DOCTYPE),document.documentElement.scrollTop和document.body.scrollTop必定有一个有值,写||,就能拿到任意有值的那一个,但是我这么写了还是0。
后来才发现,是因为布局的原因,我的滚动条不是浏览器的,是容器的。
给容器绑滚动事件,你哪个容器产生了滚动条,就给哪个绑。
父组件

	let that = this
    document.getElementById('box').addEventListener('scroll', that.scrollList, true)
  },

给每个内容区的盒子,加id
父组件

  			<div id="carda">
            <div slot="header" class="clearfix cardTitle" id="a1">
              <span style="float: left">xxx标准</span>
            </div>
           <div>内容xxxx </div>
            <div>
            <div id="cardb">
            <div slot="header" class="clearfix cardTitle" id="a1">
              <span style="float: left">xxx标准</span>
            </div>
           <div>内容xxxx </div>
            <div>
            其他省略........

然后就是在scrollList方法中写实现的代码了,这里要说一下用准备的东西了

首先要拿到有滚动条的容器的scrollTop,就是滚动条距离顶部的距离
然后拿到每个内容区的盒子距离父元素顶部的距离offsetTop,如果父元素有定位,这个offsetTop就是内容盒子顶部到父盒子顶部的距离,如果父元素没有定位,就是内容盒子顶部到文档顶部的距离。

拿到这些后,如果滚动条滚动距离在这个距离里就让它高亮的菜单

父组件

 scrollList() {
      //获取滚动条到顶部的距离
      let scrollTop = document.getElementById('box').scrollTop
      // 获取右侧每个盒子距离顶部的高度
      let height1 = document.getElementById('carda').offsetTop 
      let height2 = document.getElementById('cardb').offsetTop 
      let height3 = document.getElementById('cardc').offsetTop 
      let height4 = document.getElementById('cardd').offsetTop 
      let height5 = document.getElementById('carde').offsetTop 
      let height6 = document.getElementById('cardf').offsetTop 
      if (scrollTop < height1 && scrollTop >= 0) {
        this.$refs.leftQuick.rightId = 'a1'
      } else if (scrollTop > height1 && scrollTop < height2) {
        this.$refs.leftQuick.rightId = 'a2'
      } else if (scrollTop > height2 && scrollTop < height3) {
        this.$refs.leftQuick.rightId = 'a3'
      } else if (scrollTop > height3 && scrollTop < height4) {
        this.$refs.leftQuick.rightId = 'a4'
      } else if (scrollTop > height4 && scrollTop < height5) {
        this.$refs.leftQuick.rightId = 'a5'
      } else if (scrollTop > height5) {
        this.$refs.leftQuick.rightId = 'a6'
      }
    },

应该没有漏掉的,有问题欢迎提问~~~~~~~~~~~~~·

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值