我们先实现锚点定位,锚点定位需要借助scrollIntoView方法,这个方法会把当前元素的顶部定位到所在有滚动条的父元素的顶部,从而实现锚点定位的功能:

<style>
    .box {
      width: 500px;
    }

    .son {
      height: 500px;
      border: 1px solid #ccc;
      overflow-y: auto;
    }

    .sonItem1 {
      height: 400px;
      background-color: skyblue;
    }

    .sonItem2 {
      height: 700px;
      background-color: orange;
    }
  </style>
</head>

<body>
  <div class="box">
    <a href="baidu.com" id="aaa">跳转橙色</a>
    <div class="son">
      <div class="sonItem1">我是浅蓝</div>
      <div class="sonItem2" id="sonItem2">我是橙色</div>
    </div>
  </div>
</body>
<script>
  document.getElementById('aaa').onclick = function (e) {
    e.preventDefault(); // 阻止a标签默认跳转行为
    const dom = document.getElementById('sonItem2')
    dom.scrollIntoView()
  }
</script>

注:上面不一定要使用a标签来点击定位,其他标签的点击也是可以的,这里用是因为a标签自带点击的手势图标;

实现锚点定位并不难,只需要用scrollIntoView方法,接下来要实现滚动条滚动到哪个元素的顶部,tab就对应切换;

下面先了解几个方法的区别:scrollTopscrollHeightclientHeightoffsetTop

vant ios 输入框无法横向滚动 vant滑动切换_前端


clientHeight:带有滚动条盒子的高度

scrollHeight:带有滚动条盒子里面的滚动内容的高度

scrollTop:滚动内容被卷入的高度

offsetTop:元素顶部距离父级元素顶部的高度

判断滚动条触底:scrollHeight=scrollTop+clientHeight

判断某个元素的顶部是否到底滚动盒子的顶部:scrollTop=offsetTop-滚动条盒子距离浏览器顶部的距离(这样就可以计算出每个盒子距离带有滚动条盒子顶部的距离)

vant ios 输入框无法横向滚动 vant滑动切换_javascript_02

完整的代码示例:

<template>
  <div class="container">
    <div class="box">
      <div class="tab">
        <div class="tab_item" v-for="(item, index) in tabData" :key="item.id">
          <a href="" @click.prevent="handleTab(item.id, index + 1)">
            <div :class="num == index + 1 ? 'active' : ''">
              {{ item.title }}
            </div>
          </a>
        </div>
      </div>
      <div class="info" ref="info">
        <div
          class="info_item"
          v-for="item in tabData"
          :key="item.id"
          :ref="item.id"
        >
          <div style="background: #ffffff">{{ item.title }}</div>
          <div style="height: 500px; background: #ccc"></div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      tabData: [
        { id: '#aaa', title: '基本信息' },
        { id: '#bbb', title: '税务信息' },
        { id: '#ccc', title: '财务信息' },
        { id: '#ddd', title: '供货信息' },
        { id: '#eee', title: '人员信息' },
        { id: '#fff', title: '立案风险' }
      ],
      num: 1
    }
  },
  mounted() {
    this.$refs.info.addEventListener("scroll", this.handleScroll, true);
  },
  methods: {
    handleScroll(e) {
      let t = e.target.scrollTop;//监听的滚动条高度
      let h = e.target.scrollHeight // 滚动高度
      let c = e.target.clientHeight  // 自身高度
      // 获取dom
      const arr = []
      this.tabData.forEach(item => {
        arr.push(this.$refs[item.id][0])
      })
      if (t >= arr[0].offsetTop - 101.5 && t < arr[1].offsetTop - 101.5 && t < (h - c) - 2) {
        this.num = 1
        console.log("1", this.num, t, arr[1].offsetTop - 100.5);
      } else if (t >= arr[1].offsetTop - 101.5 && t < arr[2].offsetTop - 101.5 && t < (h - c) - 2) {
        this.num = 2
        console.log("2", this.num);
      } else if (t >= arr[2].offsetTop - 101.5 && t < arr[3].offsetTop - 101.5 && t < (h - c) - 2) {
        this.num = 3
        console.log("3", this.num);
      } else if (t >= arr[3].offsetTop - 101.5 && t < arr[4].offsetTop - 101.5 && t < (h - c) - 2) {
        this.num = 4
        console.log("4", this.num);
      } else if (t >= arr[4].offsetTop - 101.5 && t < arr[5].offsetTop - 101.5 && t < (h - c) - 2) {
        this.num = 5
        console.log("5", this.num);
      } else if (t >= arr[5].offsetTop - 101.5 && t < (h - c) - 1) {
        this.num = 6
        console.log("6", this.num);
      }
    },
    handleTab(id, num) {
      this.num = num
      console.log("锚点", this.num);
      // 锚点跳转
      this.$refs[id][0].scrollIntoView()
    }
  }
}
</script>

<style lang="scss" scoped>
* {
  box-sizing: border-box;
}
.box {
  width: 500px;
  height: 800px;
  border: 2px solid #ccc;
  background-color: #b2d4eb;
  .tab {
    margin-bottom: 20px;
    background-color: #ffffff;
    display: flex;
    width: 100%;
    overflow-x: auto;
    overflow-y: hidden;
    padding: 5px;
    .tab_item {
      div {
        border: 1px solid #ccc;
        border-radius: 3px;
        margin-right: 10px;
        padding: 3px;
        height: 30px;
        width: 80px;
        text-align: center;
        line-height: 24px;
        background-color: #d9e4ec;
      }
    }
  }
  .info {
    height: calc(800px - 80.5px);
    // background-color: blue;
    overflow: auto;
    .info_item {
      margin-bottom: 20px;
    }
  }
}
.active {
  background-color: #94bdf5 !important;
  color: #2a72a9;
}
</style>

需要注意的是(难点):当滚动条触底的时候,可能有一些盒子的高度不够,所以到达不了顶部,当继续点击tab的按钮时,会发现有些按钮明明点击了却不选中,是因为点击tab时给num赋值时和if判断里面给num赋值时冲突了,所以要加个t < (h - c) - 2)来判断是否触底了(至于这个2,可能计算不是很准确,需要微调一下),如果触底的话就不执行if判断,tab的选中完全由点击来赋值;

效果如下:

vant ios 输入框无法横向滚动 vant滑动切换_前端_03