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();
相关推荐
子兮曰5 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰5 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万5 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝5 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋5 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁5 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
汉堡大王95275 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大5 天前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端
计算机魔术师5 天前
Meta Muse agent 接入 Shopify 的 Shop Pay 实现代理式购物
前端
沙蒿同学5 天前
我用 Go 搭了一条 AI Agent 流水线:从 1 张商品图到一整套淘宝详情页
前端·javascript·后端