我们先实现锚点定位,锚点定位需要借助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就对应切换;
下面先了解几个方法的区别:scrollTop
,scrollHeight
,clientHeight
,offsetTop
clientHeight:带有滚动条盒子的高度
scrollHeight:带有滚动条盒子里面的滚动内容的高度
scrollTop:滚动内容被卷入的高度
offsetTop:元素顶部距离父级元素顶部的高度
判断滚动条触底:scrollHeight=scrollTop+clientHeight
判断某个元素的顶部是否到底滚动盒子的顶部:scrollTop=offsetTop-滚动条盒子距离浏览器顶部的距离
(这样就可以计算出每个盒子距离带有滚动条盒子顶部的距离)
完整的代码示例:
<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的选中完全由点击来赋值;
效果如下: