vue中监听页面滚动事件,实现tab栏实时更新且可定位锚点

1,067 阅读2分钟

在项目中需要实现左侧为tab栏,右侧对应页面滚动实时刷新tab,且可根据点击tab定位锚点的功能

1. scrollTop、offsetTop属性

主要涉及到两个属性scrollTop和offsetTop;
scrollTop属性定义如下:

一个元素的 scrollTop 值是这个元素的顶部到视口可见内容(的顶部)的距离的度量。当一个元素的内容没有产生垂直方向的滚动条,那么它的 scrollTop 值为0。

offsetTop属性定义如下:

HTMLElement.offsetTop 为只读属性,它返回当前元素相对于其 offsetParent 元素的顶部内边距的距离。

其中offsetParent定义为与当前元素最近的经过定位( position 不等于 static )的父级元素,具体情况如下

  • position为fixed时,offsetParent为null,offsettop的值和top相等。此时元素是以视口来定位的。
  • position非fixed,父级元素无定位(static)时,offsetParent为body。
  • position非fixed,父级元素有定位时,offsetParent为最近的有定位的父级元素。
  • body元素,offsetParent为null,offsettop为0。

2. 当前页面和实现方案介绍

这里的页面为左侧tab栏,右侧内容滚动区域。

<div class="main">
    <!-- tab区域,需要设置activeKey -->
    <div class="tabs">
        <ul>
            <li
                v-for="item in tabList"
                :key="item.key"
                :class="{active: activeKey === item.key}"
                @click="goAnchor(item.key)"
            >{{ item.title }}</li>
        </ul>
    </div>
    <!-- .content需要加上position: relative样式从而使得内部五个div的offsetTop为到.content元素顶部的距离 -->
    <div class="content">
        <!-- 五个div需要赋高度 -->
        <div class="scrollContent one" />
        <div class="scrollContent two" />
        <div class="scrollContent three" />
        <div class="scrollContent four" />
        <div class="scrollContent five" />
    </div>
</div>
<style lang="scss" scoped>
.content {
    position: relative;
    overflow-y: scroll;
}
</style>

我们可以得到五个子div相对于.content的offsetTop,与.content的scrollTop属性进行比较即可获得当前视口为哪个子div区域,从而更新activeKey显示对应的tab,这就是整体的实现思路。

3. tab栏根据页面滚动实时更新

下面看代码实现

onScroll() {
    // 获取每个子div的offsetTop
    const offsetTopArr = [];
    const anchors = document.querySelector(".scrollContent");
    anchors.forEach(i => { offsetTopArr.push(i.offsetTop) });
    // 获取当前.content区域的scrollTop
    const content = document.querySelector(".content");
    const scrollTop = content.scrollTop;
    // 比较得出当前在第几个子div
    let curIndex = 0;
    for (let i = 0; i < offsetTopArr.length; i++) {
        if (scorllTop >= offsetTopArr[i]) {
            curIndex = i;
        }
    }
    this.activeKey = this.tabList[curIndex].key;   
}

实时更新的情况需要加入监听页面滚动事件

mounted() {
    window.addEventListener("scroll", this.onScroll, true);
},
destoryed() {
    window.removeEventListener("scroll", this.onScroll);
},

或是给.content元素绑定scroll事件

<div class="content" @scroll="onScroll">

4. 定位锚点

理解了上面步骤2、3的思路再去实现定位锚点功能就轻而易举了
将当前.content的scrollTop改为tab对应子div的offsetTop,视口则会定位到对应的子div

goAnchor(key) {
    this.activeKey = key;
    const className = "." + key;
    const anchor = this.$el.querySelector(className);
    const content = document.querySelector(".content");
    content.scrollTop = anchor.offsetTop;
}