elementUI的NavMenu导航菜单实现锚点导航

624 阅读1分钟

使用elementUI的NavMenu导航菜单实现锚点导航。主要实现了点击菜单时定位到相应的内容和滚动内容时菜单定位到相应的锚点。

锚点导航的内容是接口返回的值:

<template>
    <div class="dept-info-one-container">
        <!-- 锚点导航 -->
        <el-menu :default-active="activeIndex" class="menu-item">
            <el-menu-item :index="index + ''" v-for="(item, index) in menuContent" :key="index" @click="getContent(index)">
                <span slot="title">{{ item.title }}</span>
            </el-menu-item>
        </el-menu>
        <!-- 内容 -->
        <div class="dept-info-content" ref="deptInfoRef" @scroll="getScrollContent">
            <div class="menu-content" :ref="'menuContentRef' + index" v-for="(item, index) in menuContent" :key="index">
                {{ item.content }}
                <span class="content-item"></span>
            </div>
        </div>
    </div>
</template>

<script>
export default {
    name: 'deptInfoOne',
    data() {
        return {
            activeIndex: '0',
            menuContent: [
                {
                    title: '导航一',
                    content: '内容一'
                },
                {
                    title: '导航二',
                    content: '内容二'
                },
                {
                    title: '导航三',
                    content: '内容三'
                },
                {
                    title: '导航四',
                    content: '内容四'
                },
                {
                    title: '导航五',
                    content: '内容五'
                },
                {
                    title: '导航六',
                    content: '内容六'
                },
                {
                    title: '导航七',
                    content: '内容七'
                },
                {
                    title: '导航八',
                    content: '内容八'
                }
            ],
            offsetTopValue: []
        };
    },
    mounted() {
        // 进入页面时就计算每个内容顶部至父元素顶部的距离。
        this.getOffsetTop();
    },
    methods: {
        /**
         * 点击锚点定位内容
         */
        getContent(index) {
            // 计算每个内容顶部距离父元素顶部的距离。
            let topValue = this.offsetTopValue[index];
            // 点击时滚动相应距离到相关内容。
            this.$refs.deptInfoRef.scrollTo({
                top: topValue,
                behavior: 'smooth'
            });
        },
        /**
         * 获取每个内容顶部距离父元素顶部的距离
         */
        getOffsetTop() {
            this.menuContent.forEach((value, index) => {
                this.offsetTopValue.push(this.$refs['menuContentRef' + index][0].offsetTop);
            });
        },
        /**
         * 滚动定位锚点
         */
        getScrollContent() {
            // 根据每个内容底部距离父元素顶部的距离,当滚动条滚动相关距离时,设置activeIndex的值。
            let scrollTopValue = this.$refs.deptInfoRef.scrollTop + 10;
            // 比较scrollTopValue与每个内容底部距离父元素顶部的距离,既下个内容顶部到父元素顶部的距离。当scrollTopValue小于等于该距离时,定位到该内容的锚点,退出循环。
            for (let i = 0; i <= this.offsetTopValue.length - 1; i++) {
                // 因为forEach不能跳出当前循环,所以使用for实现。
                if (scrollTopValue <= this.offsetTopValue[i + 1]) {
                    this.activeIndex = i + '';
                    return false;
                }
            }
            // 最后一个内容的锚点。
            if (this.menuContent.length == this.offsetTopValue.length) {
                this.activeIndex = this.offsetTopValue.length - 1 + '';
            }
        }
    }
};
</script>

<style lang="less">
.dept-info-one-container {
    display: flex;
    position: relative;
    height: 800px;
    font-size: 20px;
    color: #377ef9;
    .dept-info-content {
        margin-left: 20px;
        height: calc(100% - 200px);
        overflow: auto;
        &::-webkit-scrollbar {
            width: 5px; /*高宽分别对应横竖滚动条的尺寸*/
            height: 10px;
        }

        &::-webkit-scrollbar-thumb {
            /*滚动条里面小方块*/
            // border-radius: 10px;
            background: #d8d9da;
        }

        &::-webkit-scrollbar-track {
            /*滚动条里面轨道*/
            // border-radius: 10px;
            background: #d8d9da;
        }
        .menu-content {
            padding: 16px 0;
            width: calc(100% - 42px);
            .content-item {
                display: inline-block;
                margin: 20px;
                width: 100%;
                height: 800px;
                background-color: #377ef9;
            }
        }
    }
}
</style>

image.png

锚点导航的内容是组件:

<template>
    <div class="dept-info-two-container">
        <!-- 锚点导航 -->
        <el-menu :default-active="activeIndex" class="menu-item">
            <el-menu-item :index="index + ''" v-for="(item, index) in menuContent" :key="index" @click="getContent(index)">
                <span slot="title">{{ item.title }}</span>
            </el-menu-item>
        </el-menu>
        <!-- 内容 -->
        <div class="dept-info-content" ref="deptInfoRef" @scroll="getScrollContent">
            <menu-data ref="menuData"></menu-data>
            <menu-detail ref="menuDetail"></menu-detail>
            <menu-list ref="menuList"></menu-list>
            <menu-data ref="menuTable"></menu-data>
        </div>
    </div>
</template>

<script>
import MenuData from '@/views/menuInfo/menuData.vue';
import MenuDetail from '@/views/menuInfo/menuDetail.vue';
import MenuList from '@/views/menuInfo/menuList.vue';
import MenuTable from '@/views/menuInfo/menuTable.vue';

export default {
    name: 'deptInfoOne',
    components: { MenuData, MenuDetail, MenuList, MenuTable },
    data() {
        return {
            activeIndex: '0',
            menuContent: [
                {
                    id: 'menuData',
                    title: '导航一'
                },
                {
                    id: 'menuDetail',
                    title: '导航二'
                },
                {
                    id: 'menuList',
                    title: '导航三'
                },
                {
                    id: 'menuTable',
                    title: '导航四'
                }
            ],
            offsetTopValue: []
        };
    },
    mounted() {
        // 进入页面时就计算每个内容顶部至父元素顶部的距离。
        this.getOffsetTop();
    },
    methods: {
        /**
         * 点击锚点定位内容
         */
        getContent(index) {
            // 计算每个内容顶部距离父元素顶部的距离。
            let topValue = this.offsetTopValue[index];
            // 点击时滚动相应距离到相关内容。
            this.$refs.deptInfoRef.scrollTo({
                top: topValue,
                behavior: 'smooth'
            });
        },
        /**
         * 获取每个内容顶部距离父元素顶部的距离
         */
        getOffsetTop() {
            this.menuContent.forEach((value, index) => {
                this.offsetTopValue.push(this.$refs[value.id].$el.offsetTop);
            });
        },
        /**
         * 滚动定位锚点
         */
        getScrollContent() {
            // 根据每个内容底部距离父元素顶部的距离,当滚动条滚动相关距离时,设置activeIndex的值。
            let scrollTopValue = this.$refs.deptInfoRef.scrollTop + 10;
            // 比较scrollTopValue与每个内容底部距离父元素顶部的距离,既下个内容顶部到父元素顶部的距离。当scrollTopValue小于等于该距离时,定位到该内容的锚点,退出循环。
            for (let i = 0; i <= this.offsetTopValue.length - 1; i++) {
                // 因为forEach不能跳出当前循环,所以使用for实现。
                if (scrollTopValue <= this.offsetTopValue[i + 1]) {
                    this.activeIndex = i + '';
                    return false;
                }
            }
            // 最后一个内容的锚点。
            if (this.menuContent.length == this.offsetTopValue.length) {
                this.activeIndex = this.offsetTopValue.length - 1 + '';
            }
        }
    }
};
</script>

<style lang="less">
.dept-info-two-container {
    display: flex;
    position: relative;
    height: 100%;
    font-size: 20px;
    color: #377ef9;
    .dept-info-content {
        margin-left: 20px;
        height: calc(100% - 200px);
        overflow: auto;
        &::-webkit-scrollbar {
            width: 5px; /*高宽分别对应横竖滚动条的尺寸*/
            height: 10px;
        }

        &::-webkit-scrollbar-thumb {
            /*滚动条里面小方块*/
            // border-radius: 10px;
            background: #d8d9da;
        }

        &::-webkit-scrollbar-track {
            /*滚动条里面轨道*/
            // border-radius: 10px;
            background: #d8d9da;
        }
    }
}
</style>

offsetTop返回元素的顶部到最近采用定位父元素的距离。父元素的position值为relative、absoulte、fixed。

在组件的锚点导航中,获取offsetTop的值时,是在el中获取offsetTop的值,el中获取offsetTop的值,el是获取组件内部DOM元素。