实现效果:
html代码:
html
<div class="sidenav">
<ul class="nav-text progressbar">
<!-- data-target的值对应要跳转的模块的id -->
<li data-target="module1"><div class="text">锚点名称</div></li>
<li data-target="module2"><div class="text">锚点名称</div></li>
<li data-target="module3"><div class="text">锚点名称</div></li>
<li data-target="module4"><div class="text">锚点名称</div></li>
</ul>
</div>
给要跳转的dom元素id赋值即可。
css:
css
/* 侧边锚点跳转 */
.sidenav {
z-index: 40;
left: 1%;
bottom: 15%;
min-height: 55px;
position: fixed;
/* width: 140px; */
background: #ffffff;
box-shadow: 0px 0px 35px 0px rgba(106, 76, 248, 0.16);
border-radius: 8px;
padding: 25px 22px 0px 16px;
}
.progressbar li {
list-style-type: none;
/* float: left;
width: 33.33%; */
position: relative;
text-align: center;
font-size: 16px;
align-items: center;
display: flex;
height: 42px;
max-width: 102px;
margin-bottom: 25px;
}
.progressbar .text {
-webkit-box-orient: vertical;
display: -webkit-box;
/* width: 64px; */
width: 80px;
text-align: left;
-webkit-line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
color: #333333;
cursor: pointer;
}
.progressbar li:before {
content: "";
text-align: center;
width: 12px;
height: 12px;
background: #999999;
border-radius: 50%;
margin-right: 10px;
}
.progressbar li:after {
/* 伪元素实现li圆点垂直相连的线*/
top: -13px;
transform: translate(-50%, 0) rotate(90deg);
content: "";
position: absolute;
width: 66%;
height: 1px;
background: #999999;;
left: 6px;
z-index: -1;
}
.progressbar li:first-child:after {
content: none;
}
js:
js
// 避免点击事件触发时,触发滚动事件增加active类的函数
var isProgressBarClick = false;
// 获取悬浮窗的ul元素
const moduleList = document.querySelector('.progressbar');
var timeoutId = null;
// 滚动到对应模块并添加active类的点击事件处理函数
function scrollToModule(event){
isProgressBarClick = true;
var aim = event.target;
if(event.target.className === 'text'){
aim = event.target.parentNode
}
const targetModuleId = aim.getAttribute('data-target');
const targetModule = document.getElementById(targetModuleId);
if (targetModule) {
// 使用平滑滚动实现滚动效果
targetModule.scrollIntoView({ behavior: 'smooth' });
// 移除之前被添加的active类
const activeItem = document.querySelector('.progressbar li.active');
if (activeItem) {
activeItem.classList.remove('active');
}
// 为当前点击的列表项添加active类
aim.classList.add('active');
}
// 等待锚点跳转完成后再重置标志变量
clearTimeout(timeoutId);
timeoutId = setTimeout(function() {
isProgressBarClick = false;
}, 1000);
}
$('.progressbar li').click(scrollToModule)
$('.progressbar li .text').click(scrollToModule)
// 监听页面滚动事件
window.addEventListener('scroll', handleScroll);
function handleScroll() {
if(!isProgressBarClick){
// 获取页面滚动的垂直位置
const scrollPosition = window.scrollY;
// 遍历模块元素,找到当前可见的模块
var activeModuleId = null;
var moduleElements = document.querySelectorAll('.module_list>div')
for (const moduleElement of moduleElements) {
// 用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置
const { top, bottom } = moduleElement.getBoundingClientRect();
// 判断模块是否在视口内(至少一半在视口内)
if (top <= window.innerHeight / 2 && bottom >= window.innerHeight / 2) {
activeModuleId = moduleElement.id;
break;
}
}
// 添加active类
if (activeModuleId) {
// 移除之前被添加的active类
const activeItem = document.querySelector('.progressbar li.active');
if (activeItem) {
activeItem.classList.remove('active');
}
// 为当前可见的模块对应的列表项添加active类
const activeListItem = document.querySelector(`.progressbar li[data-target="${activeModuleId}"]`);
if (activeListItem) {
activeListItem.classList.add('active');
}
}
}
}
// 初始加载时触发一次滚动事件
handleScroll();