Offsettop 属性&导航锚点的实现
记录一些视窗高度和滚动的一些属性以及导航菜单跟随内容变动以及点击锚点展示内容
offsetParent 介绍:
偏移量属性
scrollHeight 和 scrollWidth ,可滚动的绝对高度,包括隐藏不可见的部分;scrollTop 和 scrollLeft :可滑动的元素,即元素出现滚动条时,内部在xy轴上滑动的距离,并且可以为其赋值,如果一个元素的内容没有产生垂直方向的滚动条,那么它的scrollTop值为0。
Vue组件简单实现导航栏锚点
<template>
<div>
<!-- 内容区域 -->
<div class="content">
<div>
content-0
</div>
<div>
content-1
</div>
<div>
content-2
</div>
<div>
content-3
</div>
<div>
content-4
</div>
</div>
<!-- 导航区域 -->
<ul class="navs">
<li :class="{active: active===0}" @click="scrollTo(0)">
content-0
</li>
<li :class="{active: active===1}" @click="scrollTo(1)">
content-1
</li>
<li :class="{active: active===2}" @click="scrollTo(2)">
content-2
</li>
<li :class="{active: active===3}" @click="scrollTo(3)">
content-3
</li>
<li :class="{active: active===4}" @click="scrollTo(4)">
content-4
</li>
</ul>
</div>
</template>
<script>
export default {
props: {},
data () {
return {
active: 0, // 当前激活的导航索引
};
},
mounted () {
// 监听滚动事件
window.addEventListener('scroll', this.onScroll, false);
},
destroy () {
// 必须移除监听器,不然当该vue组件被销毁了,监听器还在就会出错
window.removeEventListener('scroll', this.onScroll);
},
methods: {
// 滚动监听器
onScroll () {
// 获取所有锚点元素
const navContents = document.querySelectorAll('.content div');
// 所有锚点元素的 offsetTop
const offsetTopArr = [];
navContents.forEach(item => {
offsetTopArr.push(item.offsetTop);
});
// 获取当前文档流的 scrollTop
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
// 定义当前点亮的导航下标
let navIndex = 0;
for (let n = 0; n < offsetTopArr.length; n++) {
// 如果 scrollTop 大于等于第n个元素的 offsetTop 则说明 n-1 的内容已经完全不可见
// 那么此时导航索引就应该是n了
if (scrollTop >= offsetTopArr[n]) {
navIndex = n;
}
}
this.active = navIndex;
},
// 跳转到指定索引的元素
scrollTo (index) {
// 获取目标的 offsetTop
// css选择器是从 1 开始计数,我们是从 0 开始,所以要 +1
const targetOffsetTop = document.querySelector(`.content div:nth-child(${index + 1})`).offsetTop;
// 获取当前 offsetTop
let scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
// 定义一次跳 50 个像素,数字越大跳得越快,但是会有掉帧得感觉
const STEP = 50;
// 判断是往下滑还是往上滑
if (scrollTop > targetOffsetTop) {
// 往上滑
smoothUp();
} else {
// 往下滑
smoothDown();
}
// 定义往下滑函数
function smoothDown () {
// 如果当前 scrollTop 小于 targetOffsetTop 说明视口还没滑到指定位置
if (scrollTop < targetOffsetTop) {
// 如果和目标相差距离大于等于 STEP 就跳 STEP
// 否则直接跳到目标点,目标是为了防止跳过了。
if (targetOffsetTop - scrollTop >= STEP) {
scrollTop += STEP;
} else {
scrollTop = targetOffsetTop;
}
document.body.scrollTop = scrollTop;
document.documentElement.scrollTop = scrollTop;
// 关于 requestAnimationFrame 可以自己查一下,在这种场景下,相比 setInterval 性价比更高
requestAnimationFrame(smoothDown);
}
}
// 定义往上滑函数
function smoothUp () {
if (scrollTop > targetOffsetTop) {
if (scrollTop - targetOffsetTop >= STEP) {
scrollTop -= STEP;
} else {
scrollTop = targetOffsetTop;
}
document.body.scrollTop = scrollTop;
document.documentElement.scrollTop = scrollTop;
requestAnimationFrame(smoothUp);
}
}
},
},
};
</script>
<style scoped>
/* 内容区的样式 */
.content {
background-color: white;
width: 500px;
}
.content div {
width: 100%;
height: 600px;
font-size: 36px;
padding: 20px;
background-color: #7ec384;
}
.content div:nth-child(2n) {
background-color: #847ec3;
}
/* 导航栏的样式 */
.navs {
position: fixed;
top: 80px;
left: 700px;
background-color: #efefef;
}
.navs li {
padding: 0 20px;
line-height: 1.6;
font-size: 24px;
}
/* 当导航被点亮后改变颜色 */
.navs .active{
color: #847ec3;
background-color: #e2e2e2;
}
</style>
受到启发,暨大官网的那个帆船跟着标题高亮走的效果也试了一下,纯h5写
<div style="display: inline-block;">
<ul id="nav">
<li class="nav-li">
<a href="#">学校概况</a>
</li>
<li class="nav-li">
<a href="#">组织机构</a>
</li>
<li class="nav-li">
<a href="#">招生就业</a>
</li>
<li class="nav-li">
<a href="https://faculty.jnu.edu.cn">人才培养</a>
<div class="arrow-top"></div>
</li>
<li class="nav-li">
<a href="https://hrdam.jnu.edu.cn/298/list.htm">人才招聘</a>
</li>
<li class="nav-li">
<a href="#">科学研究</a>
</li>
<li class="nav-li">
<a href="https://wsc.jnu.edu.cn/">合作交流</a>
</li>
<li class="nav-li">
<a href="#">综合服务</a>
</li>
</ul>
</div>
</div>
样式,自己随便写写
#nav {
padding-left: 130px;
height: 40px;
line-height: 40px;
/* margin: 0 auto; */
display: flex;
white-space: nowrap;
/* background: url(./img/hw_zxdj.png) left no-repeat;
background-size: auto 100%;
background-position: 100px 0px; */
}
#nav .nav-li {
flex: 1;
padding-right: 20px;
/* width: 12.5%; */
}
#nav li {
display: inline-block;
padding-left: 10px;
height: 40px;
font-size: 18px;
text-align: left;
z-index: 990;
}
#nav li a {
position: relative;
float: left;
width: 100%;
text-decoration: none;
color: #fff;
z-index: 999;
}
.arrow-top {
position: absolute;
top: -80px;
width: 150px;
height: 80px;
background: url(./img/top_ship.png) bottom left no-repeat;
background-size: 100%;
z-index: 998;
background-position: -26px 10px;
transition: all 1s ease-in-out;
}
操作控制offsetLeft 有些层级可能不太对,帆船图片也要自己找
function handleMoveShip () {
let liList = document.querySelectorAll("#nav li");
let listContent = document.querySelector(".menu-nav");
let dropBlock = document.querySelectorAll(".drop-block ul");
let ship = document.getElementsByClassName("arrow-top")[0];
let dropList = document.getElementsByClassName("menu-block")[0];
for(let i= 0, len = liList.length; i < len; i++) {
//
liList[i].addEventListener('mouseover', function(event) {
console.log('mouseover', event);
ship.style.left = liList[i].offsetLeft + 'px';
dropList.style.display = 'block';
dropBlock[i].setAttribute('class', 'active');
})
}
liList.forEach((item,index) => {
item.addEventListener('mouseleave', () => {
dropBlock[index].setAttribute('class', '');
})
})
listContent.addEventListener('mouseleave', function (event) {
dropList.style.display = 'none';
})
dropBlock.forEach((item, index) => {
item.addEventListener('mouseover', (e) => {
item.setAttribute('class', 'active');
ship.style.left = liList[index].offsetLeft + 'px';
})
})
dropBlock.forEach(item => {
item.addEventListener('mouseleave', () => {
item.setAttribute('class', '');
})
})
}