css伪元素实现li列表圆点相连+锚点跳转悬浮窗实现

实现效果:

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();
相关推荐
栈老师不回家1 小时前
Vue 计算属性和监听器
前端·javascript·vue.js
前端啊龙1 小时前
用vue3封装丶高仿element-plus里面的日期联级选择器,日期选择器
前端·javascript·vue.js
一颗松鼠1 小时前
JavaScript 闭包是什么?简单到看完就理解!
开发语言·前端·javascript·ecmascript
小远yyds1 小时前
前端Web用户 token 持久化
开发语言·前端·javascript·vue.js
吕彬-前端2 小时前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱2 小时前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai2 小时前
uniapp
前端·javascript·vue.js·uni-app
帅比九日3 小时前
【HarmonyOS Next】封装一个网络请求模块
前端·harmonyos
bysking3 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
王哲晓4 小时前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js