[场景实现]:左侧目录树右侧内容联动

1、需求描述

左侧是目录,部分目录项有子项,右侧是内容。
当滑动右侧内容区域的时候,最上部分的内容对应的左侧目录项会有样式背景色区分。
当点击左侧目录项的时候,右侧对应的内容会滚动到顶部。
在这里插入图片描述

2、实现思路

锚点来做对应。
左侧目录树用的el-menu,用.el-menu-item.is-active来做样式区分。
点击和滚动的冲突用setTimeout解决。
该部分实现在弹窗上,用ref和watch。避免挂载时据id找不到元素。
最后一个目录项无法滚动到顶部,但还是需要样式显示,单独设置。

3、关键代码

总体布局样式等

...
<!--弹窗等已省略-->
<div class="editBox">
	<!--左侧-->
  <div class="sidebar">
    <el-menu
      :default-active="activeIndex"
      style="margin-top: 22px;padding-right: 10px;border:none"
    >
      <template v-for="(item, index) in menuItems">
        <el-menu-item
          v-if="!item.subItems"
          :key="`menu-item-${index}`"
          :index="`${index}`"
          @click="handleMenuItemClick(index)"
        >
          {{ item.title }}
        </el-menu-item>
        <el-sub-menu
          v-else
          :key="`submenu-${index}`"
          :index="`${index}`"
        >
          <template #title>
            {{ item.title }}
          </template>
          <el-menu-item
            v-for="(subItem, subIndex) in item.subItems"
            :key="`sub-item-${index}-${subIndex}`"
            :index="`${index}-${subIndex}`"
            @click="handleMenuItemClick(index, subIndex)"
          >
            {{ subItem.title }}
          </el-menu-item>
        </el-sub-menu>
      </template>
    </el-menu>
  </div>
  <!--右侧-->
  <div
    id="contentMain"
    ref="contentMain"
    class="content"
    @scroll="handleScroll"
  >
    <!--  内容子组件start    -->
    <partOne/>
    <partTwo/>
    <partThree/>
    ...
    <!--   内容子组件end   -->
  </div>
</div>

<!--   样式/deep/或::v-deep   -->
...
/deep/ .el-menu-item.is-active {
  background-color: #6991FF !important;
  border-radius: 4px;
  color: #fff;
  span {
    color: #fff !important;
  }
}
...

内容子组件示例

//目录项 对应menuItems 设置id和class
<div>
	<div id="partTwo" class="content-section">内容二</div>
	...
	//如果有目录子项,对应menuItems 设置id和class
	<el-row id="subOne"
	        class="sub-section-1">
	  <el-col class="titleItem">子项1 </el-col>
	</el-row>
	...
</div>

滚动点击事件等

const contentMain= ref(null);//对应该部分的ref
//anchor即分别对应内容部分开始处的id
const menuItems = [
    {
        title: '内容一',
        anchor: 'partOne'
    },
    {
        title: '内容二',
        anchor: 'partTwo',
        subItems: [
            {
                title: '子项1',
                anchor: 'subOne'
            },
            {
                title: '子项2',
                anchor: 'subTwo'
            },
            {
                title: '子项3',
                anchor: 'subThree'
            }
        ]
    },
    ...
];

const activeIndex = ref('0');//当前激活项
const scroll = ref(true); //是否可滚动

//点击事件
const handleMenuItemClick = (index, subIndex = null) => {
    scroll.value = false; // 禁止滚动
    activeIndex.value = index; // 设置当前菜单项的索引
    if (subIndex !== null) {
        activeIndex.value = `${index}-${subIndex}`; // 设置当前子菜单项的索引
    }

    const { anchor } = subIndex !== null ? menuItems[index].subItems[subIndex] : menuItems[index]; // 获取菜单项或子菜单项对应的锚点
    const content = document.getElementById(anchor); // 找到锚点对应的内容

	//利用setTimeout来避免点击之后滚动与点击带来的跳动,区分最后一个目录项
    if (content && !scroll.value) {
        if (index === menuItems.length - 1) {
            const contentContainer = document.getElementById('contentMain');
            const offset = contentContainer.scrollHeight - contentContainer.clientHeight;
            contentContainer.scrollTo({ top: offset, behavior: 'smooth' });
        } else {
            content.scrollIntoView({ behavior: 'smooth', block: 'start' });
        }
        setTimeout(() => {
            scroll.value=true;
        }, 2000);
    }
};

//滚动事件
const handleScroll = () => {
	//目录项class对应元素
    const contentSections = document.querySelectorAll('.content-section');
    let minDistance = Infinity;
    let currentSectionIndex = 0;//当前默认选中的父项
    let currentSubSectionIndex = 0;//当前默认选中的子项
    
	//内容区域的元素
    const dialog = document.getElementById('contentMain');
	//该部分实现在弹框而非整个视口,要减去
    contentSections.forEach((section, index) => {
        const distanceMain = Math.abs(section.getBoundingClientRect().top - dialog.getBoundingClientRect().top);
        if (distanceMain < minDistance) {
            minDistance = distanceMain;
            currentSectionIndex = index;
            if (menuItems[currentSectionIndex].subItems) {
            	//子目录项对应class的元素
                const subSections = document.querySelectorAll(`.sub-section-${currentSectionIndex}`);
                subSections.forEach((subSection, subIndex) => {
                    const distanceSub = Math.abs(subSection.getBoundingClientRect().top - dialog.getBoundingClientRect().top);
                    if (distanceSub < minDistance) {
                    //比较所有的目录项及子目录项找到离区域顶部最近的
                        minDistance = distanceSub;
                        currentSectionIndex = index;
                        currentSubSectionIndex = subIndex;
                    }
                });
            }
        }
    });

	//避免点击之后滚动与点击带来的跳动
    if (scroll.value) {
    //当前被激活项赋值
        activeIndex.value = currentSectionIndex;
        if (menuItems[currentSectionIndex].subItems) {
            activeIndex.value = `${currentSectionIndex}-${currentSubSectionIndex}`;
        }
        const content = document.getElementById('contentMain');
        //为最后一个目录项单独设置
        if (content.scrollTop + content.clientHeight >= content.scrollHeight) {
            activeIndex.value = menuItems.length - 1;
        }
    }
};
//该部分实现在弹框上,用watch避免直接在挂载的时候找元素找不到
watch(() => contentMain.value, (newValue, oldValue) => {
    if (newValue) {
        contentMain.value.addEventListener('scroll', handleScroll);
    }
});
//如果非弹框,挂载的时候通过id来找元素监听即可
//onMounted(() => //{document.getElementById('contentMain').addEventListener('scroll',handleScroll);});

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/396510.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

hive load data未正确读取到日期

1.源数据CSV文件日期字段值&#xff1a; 2.hive DDL语句&#xff1a; CREATE EXTERNAL TABLE test.textfile_table1(id int COMMENT ????, name string COMMENT ??, gender string COMMENT ??, birthday date COMMENT ????,.......) ROW FORMAT SERDE org.apache.…

day06-股票流水定时多线程采集实现

股票流水定时多线程采集实现 学习目标 1.分析并理解当前股票采集功能存在的问题; 2.理解XXLJOB的使用场景和使用流程; 2.1 掌握xxljob的基本使用流程; 2.2 理解cron表达式; 3.理解xxljob集成到项目的基本流程; 3.1 自定义任务执行器; 3.2 分析国内大盘的开盘周期&#xff0c;…

pytorch框架:pytorch的钩子

说明 在深度学习中&#xff0c;"钩子"通常指的是在模型训练或推理过程中插入的一些回调函数或处理程序&#xff0c;以执行额外的操作或监控模型的行为。这些钩子可以分为两种类型&#xff1a;张量钩子和模块钩子。 张量钩子&#xff08;Tensor Hooks&#xff09;&am…

读十堂极简人工智能课笔记07_模拟与情感

1. 数码式考察 1.1. 制作计算机动画或游戏 1.1.1. 想怎么制作都可以 1.2. 计算机模拟 1.2.1. 目标是建造一个虚拟的实验室&#xff0c;其行为与现实完全一致&#xff0c;只是某些变量由我们来控制 1.3. 对现实世界进行建模并不容易&#xff0c;需要非常谨慎地收集和使用数…

微信小程序 搜索框实现模糊搜索(带模拟数据,js,wxml,wxss齐全)

最近在做一个小程序的页面&#xff0c;搜索框困扰了我很久&#xff0c;今天终于把搜索框给做了出来&#xff0c;记录一下过程 我主要使用的就是wx的if&#xff0c;当我输入框用户点击的时候&#xff0c;我前面的显示界面添加上false属性&#xff0c;然后我搜索页面显示出true的…

gRPC 备查

简介 HTTP/2 HTTP/2 的三个概念 架构 使用流程 gRPC 的接口类型 1.单一RPC 2.服务器流式RPC 3.客户端式流式RPC 4.双向流式RPC

【ARMv8M Cortex-M33 系列 8 -- RT-Thread 移植 posix pthread】

文章目录 RT-Thread POSIX PthreadRT-Thread Pthread 相关宏定义RT-Thread libc 初始化RT-Thread Pthread 测试 RT-Thread POSIX Pthread pthread是POSIX&#xff08;Portable Operating System Interface&#xff09;标准定义的一套线程相关的API&#xff0c;全称为POSIX Thr…

PDF控件Spire.PDF for .NET【安全】演示:如何在 PDF 中添加签名字段

Spire.PDF for .NET 是一款独立 PDF 控件&#xff0c;用于 .NET 程序中创建、编辑和操作 PDF 文档。使用 Spire.PDF 类库&#xff0c;开发人员可以新建一个 PDF 文档或者对现有的 PDF 文档进行处理&#xff0c;且无需安装 Adobe Acrobat。 E-iceblue 功能类库Spire 系列文档处…

Golang - 使用CentOS 7 安装Golang环境

文章目录 操作步骤 操作步骤 为在CentOS 7上安装Go语言环境&#xff0c;可以按照以下步骤进行操作&#xff1a; 下载Go语言包&#xff1a; 从官方网站 https://golang.org/dl/ 下载适用于Linux的Go语言包。 解压缩Go语言包&#xff1a; 使用以下命令解压缩下载的Go语言包 […

刷题Day3

&#x1f308;个人主页&#xff1a;小田爱学编程 &#x1f525; 系列专栏&#xff1a;刷题日记 &#x1f3c6;&#x1f3c6;关注博主&#xff0c;随时获取更多关于IT的优质内容&#xff01;&#x1f3c6;&#x1f3c6; &#x1f600;欢迎来到小田代码世界~ &#x1f601; 喜欢…

给label-studio 配置sam(segment anything)ml 记录

给label-studio 配置sam&#xff08;segment anything&#xff09;ml 后端记录 配置ml后台下载代码下载模型文件创建环境模型转换后端服务启动 配置label-studio 前端配置模型后端连接配置标注模板标注界面使用 参考链接 配置ml后台 下载代码 git clone https://github.com/H…

机器学习---规则学习(一阶规则学习、归纳逻辑程序设计)

1. 一阶规则学习 “一阶”的目的&#xff1a;描述一类物体的性质、相互关系&#xff0c;比如利用一阶关系来挑“ 更好的”瓜&#xff0c;但实际应用 中很难量化颜色、 …、敲声的属性值。一般情况下可以省略全称量词。 命题逻辑&#xff1a;属性-值数据 色泽程度&#xff1a…

2.19学习总结

1.中位数 2.统计和 3.铺设道路 4.岛屿个数 5.冶炼金属 6.飞机降落 7.接龙数列 中位数https://www.luogu.com.cn/problem/P1168 题目描述 给定一个长度为 &#xfffd;N 的非负整数序列 &#xfffd;A&#xff0c;对于前奇数项求中位数。 输入格式 第一行一个正整数 &#xfff…

Spring Boot与LiteFlow:轻量级流程引擎的集成与应用含完整过程

点击下载《Spring Boot与LiteFlow&#xff1a;轻量级流程引擎的集成与应用含完整过程》添加链接描述 1. 前言 本文旨在介绍Spring Boot与LiteFlow的集成方法&#xff0c;详细阐述LiteFlow的原理、使用流程、步骤以及代码注释。通过本文&#xff0c;读者将能够了解LiteFlow的特…

【LeetCode: 590. N 叉树的后序遍历 + DFS】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

163邮箱发邮件

1、Jenkins安装Email Extension Plugin 2、网易邮箱里获取授权码:qa_jenkins_robot@163.com 开启POP3/SMTP 我已经配置过了,所以这里会有一个使用设备 3、配置Jenkins邮箱通知 Manage Jnekins-Configuration System Jenkins Location: Extended E-mail Notification: …

FL Studio21中文版本混音功能介绍

FL Studio 21的混音功能是其音乐制作能力中不可或缺的一部分&#xff0c;它为用户提供了强大的工具&#xff0c;以便他们可以对音轨进行细致的调整&#xff0c;确保音乐作品的最终呈现效果达到最佳。 FL Studio 21 Win-安装包下载如下: https://wm.makeding.com/iclk/?zonei…

图形渲染基础学习

原文链接&#xff1a;游戏开发入门&#xff08;三&#xff09;图形渲染_如果一个面只有三个像素进行渲染可以理解为是定点渲染吗?-CSDN博客 游戏开发入门&#xff08;三&#xff09;图形渲染笔记&#xff1a; 渲染一般分为离线渲染与实时渲染&#xff0c;游戏中我们用的都是…

指针的进阶(C语言)(上)

目录 前言 1、字符指针 2、指针数组 3、数组指针 3.1数组指针的定义 3.2 数组名VS&数组名 3.3数组指针的运用 前言 对于指针&#xff0c;我们已经有了初步认识&#xff08;可以看我写的指针详解那一篇文章&#xff09;。 简单总结一下基本概念&#xff1a; 1、指针就…

探索海洋世界,基于YOLOv5全系列【n/s/m/l/x】参数模型开发构建海洋场景下海洋生物检测识别分析系统

前面的博文中&#xff0c;开发实践过海底相关生物检测识别的项目&#xff0c;对于海洋场景下的海洋生物检测则很少有所涉及&#xff0c;这里本文的主要目的就是想要开发构建基于YOLOv5的海洋场景下的海洋生物检测识别系统。 前文相关的开发实践如下&#xff0c;感兴趣的话可以…
最新文章