scrollIntoView:点击导航跳转至对应的页面,页面滚动时高亮导航

393 阅读1分钟

一、效果

  1. 点击导航可以跳转至对应的页面

动画.gif

  1. 页面滚动时高亮导航

动画.gif

二、实现

  1. 点击导航可以跳转至对应的页面
    handleNav(item) {
      const ele = document.getElementById(item.component)
      this.activeComp = item.component
      ele.scrollIntoView({ behavior: 'smooth', block: 'start' })
    },
  1. 页面滚动时高亮导航
    // 初始化:设置每个标题的top值
    init() {
      this.$nextTick(() => {
        const firstEle = this.componentList[0].component
        const firstTop = document.getElementById(firstEle).offsetTop
        this.componentList.forEach((item) => {
          const ele = document.getElementById(item.component)
          item.top = ele.offsetTop - firstTop
        })
      })
    },
    
    
    给滚动的dom元素添加事件: @scroll="handleScroll"
    // 页面滚动,高亮nav
    handleScroll(e) {
      this.componentList.forEach((item) => {
        if (item.top < e.target.scrollTop) {
          this.activeComp = item.component
        }
      })
    }

三、完整代码

<template>
  <div class="product-add h-full pl-30 flex flex-col">
    <el-steps :active="activeStep" finish-status="success" class="w-300 mx-auto my-20">
      <el-step title="选择产品类型" />
      <el-step title="填写产品详情" />
    </el-steps>
    <div class="flex-1 overflow-y-auto flex flex-col">
      <div class="flex-1 overflow-y-auto pr-120 pb-100" @scroll="handleScroll">
        <div v-for="item of componentList" :key="item.component">
          <h1 class="my-20 pl-8 text-16 border-l-4 border-solid border-green" :id="item.component">{{ item.title }}</h1>
          <component :is="item.component" ref="comRef" />
        </div>
      </div>
      <ul class="nav-wrapper">
        <li v-for="item of componentList" :key="item.component" @click="handleNav(item)" :class="item.component === activeComp && 'active'">
          {{ item.title }}
        </li>
      </ul>
    </div>
  </div>
</template>

<script>
import BaseInfo from './components/BaseInfo' // 基本信息
import SpecInfo from './components/SpecInfo' // 规格信息
import ProductInfo from './components/ProductInfo' // 产品信息
import ProductPrice from './components/ProductPrice' // 产品计价
import PrintArea from './components/PrintArea' // 印刷区域维护
import PrintPrice from './components/PrintPrice' // 印刷计价
import { mapState, mapMutations, mapGetters } from 'vuex'
export default {
  data() {
    return {
      activeStep: 1, // 0=选择产品类型 1=填写产品详情
      componentList: [
        { title: '基本信息', component: 'BaseInfo' },
        { title: '规格信息', component: 'SpecInfo' },
        { title: '产品信息', component: 'ProductInfo' },
        { title: '产品计价', component: 'ProductPrice' },
        { title: '印刷区域维护', component: 'PrintArea' },
        { title: '印刷计价', component: 'PrintPrice' }
      ],
      activeComp: 'BaseInfo' // 当前高亮的nav
    }
  },
  computed: {
    ...mapState({ productAdd: (state) => state.productAdd })
  },
  watch: {
    'productAdd.specInfo.tableData': {
      handler() {
        this.init()
      },
      deep: true,
      immediate: true
    }
  },
  methods: {
    // 初始化:设置每个标题的top值
    init() {
      this.$nextTick(() => {
        const firstEle = this.componentList[0].component
        const firstTop = document.getElementById(firstEle).offsetTop
        this.componentList.forEach((item) => {
          const ele = document.getElementById(item.component)
          item.top = ele.offsetTop - firstTop
        })
      })
    },
    // 点击导航栏滚动至对应的页面
    handleNav(item) {
      const ele = document.getElementById(item.component)
      this.activeComp = item.component
      ele.scrollIntoView({ behavior: 'smooth', block: 'start' })
    },
    // 页面滚动,高亮nav
    handleScroll(e) {
      this.componentList.forEach((item) => {
        if (item.top < e.target.scrollTop) {
          this.activeComp = item.component
        }
      })
    }
  },
  components: { BaseInfo, SpecInfo, ProductInfo, ProductPrice, PrintArea, PrintPrice }
}
</script>

<style lang="scss" scoped>
@import './index.scss';
</style>

css:

  .nav-wrapper {
    position: fixed;
    right: 26px;
    top: 50%;
    transform: translateY(-50%);
    z-index: 100;
    li {
      font-size: 14px;
      text-align: right;
      line-height: 24px;
      cursor: pointer;
      color: #555;
      &.active {
        color: #009944;
      }
    }
  }

注意点:

  1. 当手动添加或减少规格值时,需要重新执行init函数,获取到最新的top值,如果你的页面是固定的,那么只需要初始化时执行一遍init即可
  2. 在获取top值时,需要减去第一个title的offsetTop,如果你的页面的title前面没有元素,那么可以不用减
  3. 如果遇到滚动时,高亮的title显示不准确,可以考虑用requestAnimationFrame进行优化

如果你觉得这篇文章对你有用,可以看看作者封装的库xtt-utils,里面封装了非常实用的js方法。如果你也是vue开发者,那更好了,除了常用的api,还有大量的基于element-ui组件库二次封装的使用方法和自定义指令等,帮你提升开发效率。不定期更新,欢迎交流~