iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >如何使用Vue3实现文章目录功能
  • 847
分享到

如何使用Vue3实现文章目录功能

2023-06-29 12:06:43 847人浏览 薄情痞子
摘要

这篇文章主要为大家展示了“如何使用vue3实现文章目录功能”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如何使用Vue3实现文章目录功能”这篇文章吧。前言这一段时间一直在做一个博客项目 

这篇文章主要为大家展示了“如何使用vue3实现文章目录功能”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如何使用Vue3实现文章目录功能”这篇文章吧。

前言

这一段时间一直在做一个博客项目 Kila Kila Blog,找了一圈发现没有特别满足自己需求的目录组件,所以决定自己动手,完成一个满足以下预期目标的目录组件:

  • 自动高亮选中当前正在阅读的章节

  • 自动展开当前正在阅读的章节的子标题,并隐藏其他章节的子标题

  • 显示阅读进度

完成后的目录组件如下图左侧所示:

如何使用Vue3实现文章目录功能

实现过程

由于标题之间有父子的关系,所以我们应该用树数据结构来解决这个问题。我们遍历文章容器中的所有标签,如果遇到 <h2><h3>这类标签,就创建一个节点,将其放到列表中,之后使用 v-for 指令来生成目录就行了。下面分析一下每个节点需要有哪些属性。

一个树的节点,应该具有的属性包括:父节点的指针 parent、子节点的指针列表 children,因为一个节点代表一个标题,所以还要包含:标题的 ID号 id(用于 v-for 的 key),标题名 name(添加了标题的序号)、原始标题名 rawName 和标题的可见性 isVisible,当我们点击标题时,应该滚动到标题的位置,所以还要有 scrollTop 属性。在我们遍历文章容器中的所有标签时,需要判断当前遇到的标签和上一个标签之间的父子关系,所以要有一个 level 属性代表每一个节点的等级。下面是具体实现代码:

<template>    <div class="catalog-card" v-if="Object.keys(titles).length > 0">        <div class="catalog-card-header">            <div>                <span                    ><font-awesome-icon                        :icon="['fas', 'bars-staggered']"                        class="catalog-icon"                /></span>                <span>目录</span>            </div>            <span class="progress">{{ progress }}</span>        </div>        <div class="catalog-content">            <div                v-for="title in titles"                :key="title.id"                @click="scrollToView(title.scrollTop)"                :class="[                    'catalog-item',                    currentTitle.id == title.id ? 'active' : 'not-active',                ]"                :                v-show="title.isVisible"                :title="title.rawName"            >                {{ title.name }}            </div>        </div>    </div></template><script>import { Reactive, ref } from "vue";export default {    name: "KilaKilaCatalog",    setup(props) {        let titles = reactive(getTitles());        let currentTitle = reactive({});        let progress = ref(0);        // 获取目录的标题        function getTitles() {            let titles = [];            let levels = ["h2", "h3", "h4"];            let articleElement = document.querySelector(props.container);            if (!articleElement) {                return titles;            }            let elements = Array.from(articleElement.querySelectorAll("*"));            // 调整标签等级            let tagNames = new Set(                elements.map((el) => el.tagName.toLowerCase())            );            for (let i = levels.length - 1; i >= 0; i--) {                if (!tagNames.has(levels[i])) {                    levels.splice(i, 1);                }            }            let serialNumbers = levels.map(() => 0);            for (let i = 0; i < elements.length; i++) {                const element = elements[i];                let tagName = element.tagName.toLowerCase();                let level = levels.indexOf(tagName);                if (level == -1) continue;                let id = tagName + "-" + element.innerText + "-" + i;                let node = {                    id,                    level,                    parent: null,                    children: [],                    rawName: element.innerText,                    scrollTop: element.offsetTop,                };                if (titles.length > 0) {                    let lastNode = titles.at(-1);                    // 遇到子标题                    if (lastNode.level < node.level) {                        node.parent = lastNode;                        lastNode.children.push(node);                    }                    // 遇到上一级标题                    else if (lastNode.level > node.level) {                        serialNumbers.fill(0, level + 1);                        let parent = lastNode.parent;                        while (parent) {                            if (parent.level < node.level) {                                parent.children.push(node);                                node.parent = parent;                                break;                            }                            parent = parent.parent;                        }                    }                    // 遇到平级                    else if (lastNode.parent) {                        node.parent = lastNode.parent;                        lastNode.parent.children.push(node);                    }                }                serialNumbers[level] += 1;                let serialNumber = serialNumbers.slice(0, level + 1).join(".");                node.isVisible = node.parent == null;                node.name = serialNumber + ". " + element.innerText;                titles.push(node);            }            return titles;        }        // 监听滚动事件并更新样式        window.addEventListener("scroll", function () {            progress.value =                parseInt(                    (window.scrollY / document.documentElement.scrollHeight) *                        100                ) + "%";            let visibleTitles = [];            for (let i = titles.length - 1; i >= 0; i--) {                const title = titles[i];                if (title.scrollTop <= window.scrollY) {                    if (currentTitle.id === title.id) return;                    Object.assign(currentTitle, title);                    // 展开节点                    setChildrenVisible(title, true);                    visibleTitles.push(title);                    // 展开父节点                    let parent = title.parent;                    while (parent) {                        setChildrenVisible(parent, true);                        visibleTitles.push(parent);                        parent = parent.parent;                    }                    // 折叠其余节点                    for (const t of titles) {                        if (!visibleTitles.includes(t)) {                            setChildrenVisible(t, false);                        }                    }                    return;                }            }        });        // 设置子节点的可见性        function setChildrenVisible(title, isVisible) {            for (const child of title.children) {                child.isVisible = isVisible;            }        }        // 滚动到指定的位置        function scrollToView(scrollTop) {            window.scrollTo({ top: scrollTop, behavior: "smooth" });        }        return { titles, currentTitle, progress, scrollToView };    },    props: {        container: {            type: String,            default: ".post-body .article-content",        },    },};</script><style lang="less" scoped>.catalog-card {    background: white;    border-radius: 8px;    box-shadow: 0 3px 8px 6px rgba(7, 17, 27, 0.05);    padding: 20px 24px;    width: 100%;    margin-top: 25px;    box-sizing: border-box;}.catalog-card-header {    text-align: left !important;    margin-bottom: 15px;    display: flex;    justify-content: space-between;    align-items: center;}.catalog-icon {    font-size: 18px;    margin-right: 10px;    color: dodgerblue;}.catalog-card-header div > span {    font-size: 17px;    color: #4c4948;}.progress {    color: #a9a9a9;    font-style: italic;    font-size: 140%;}.catalog-content {    max-height: calc(100vh - 120px);    overflow: auto;    margin-right: -24px;    padding-right: 20px;}.catalog-item {    color: #666261;    margin: 5px 0;    line-height: 28px;    cursor: pointer;    transition: all 0.2s ease-in-out;    font-size: 14px;    padding: 2px 6px;    display: -WEBkit-box;    overflow: hidden;    text-overflow: ellipsis;    -webkit-line-clamp: 1;    -webkit-box-orient: vertical;    &:hover {        color: #1892ff;    }}.active {    background-color: #;    color: white;    &:hover {        background-color: #0c82e9;        color: white;    }}</style>

以上是“如何使用Vue3实现文章目录功能”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注编程网精选频道!

--结束END--

本文标题: 如何使用Vue3实现文章目录功能

本文链接: https://www.lsjlt.com/news/324751.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 如何使用Vue3实现文章目录功能
    这篇文章主要为大家展示了“如何使用Vue3实现文章目录功能”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“如何使用Vue3实现文章目录功能”这篇文章吧。前言这一段时间一直在做一个博客项目 ...
    99+
    2023-06-29
  • 使用 Vue3 实现文章目录功能
    前言 这一段时间一直在做一个博客项目 Kila Kila Blog,找了一圈发现没有特别满足自己需求的目录组件,所以决定自己动手,完成一个满足以下预期目标的目录组件: 自动...
    99+
    2024-04-02
  • 如何使用Typecho插件实现添加文章目录
    这篇文章主要介绍了如何使用Typecho插件实现添加文章目录的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇如何使用Typecho插件实现添加文章目录文章都会有所收获,下面我们一起来看看吧。注意:我使用的是Joe...
    99+
    2023-07-05
  • WordPress如何实现相关文章功能
    本篇内容主要讲解“WordPress如何实现相关文章功能”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“WordPress如何实现相关文章功能”吧! ...
    99+
    2023-03-02
    wordpress
  • 基于Vue3和elementplus如何实现登录功能
    这篇文章主要介绍了基于Vue3和elementplus如何实现登录功能的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇基于Vue3和elementplus如何实现登录功能文章都会有所收获,下面我们一起来看看吧。登...
    99+
    2023-07-05
  • 怎么使用Typecho插件实现添加文章目录
    本文小编为大家详细介绍“怎么使用Typecho插件实现添加文章目录”,内容详细,步骤清晰,细节处理妥当,希望这篇“怎么使用Typecho插件实现添加文章目录”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。注意:我使...
    99+
    2023-07-05
  • PHP如何实现“相关文章推荐”功能
    本篇内容介绍了“PHP如何实现“相关文章推荐”功能”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!通常在做内容网站的时候,需要在每一篇文章中出...
    99+
    2023-06-20
  • 如何使用Vue3+Vant组件实现App搜索历史记录功能
    这篇文章给大家分享的是有关如何使用Vue3+Vant组件实现App搜索历史记录功能的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。最近在开发一款新的app项目,我自己也是第一次接触app开发,经过团队的一段时间研究...
    99+
    2023-06-15
  • vue3使用vuedraggable实现拖拽功能
    本文实例为大家分享了vue3使用vuedraggable实现拖拽功能的具体代码,供大家参考,具体内容如下 1、npm i vuedraggable -S,使用这个命令,vue3会报错...
    99+
    2024-04-02
  • vue3如何实现PDF文件在线预览功能
    目录概述正文创建 vue3 项目添加 PDF 预览插件总结概述 之前我们用 Reactjs + React-PDF 实现了 React 版的 PDF 文件预览,今天我们用 Vue3 ...
    99+
    2024-04-02
  • Vue3实现登录表单验证功能
    目录一.实现思路与效果图二.实现具体过程三.完整代码与效果图一.实现思路与效果图 使用async-validator 插件,阿里出品的 antd 和 ElementUI 组件库中表单...
    99+
    2024-04-02
  • Vue3通用API功能如何使用
    这篇文章主要介绍了Vue3通用API功能如何使用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Vue3通用API功能如何使用文章都会有所收获,下面我们一起来看看吧。通用APIversion (暴漏当前使用的Vu...
    99+
    2023-07-06
  • 使用struts1如何实现一个登录功能
    这期内容当中小编将会给大家带来有关使用struts1如何实现一个登录功能,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。环境:MyEclipse 14    &nbs...
    99+
    2023-05-31
    struts 登录
  • SSM项目中如何使用拦截器实现登录验证功能
    小编给大家分享一下SSM项目中如何使用拦截器实现登录验证功能,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!登录接口实现public User ...
    99+
    2023-06-28
  • 使用Vue3+PDF.js实现PDF预览功能
    目录1 前言2 PDF 预览测试2.1 下载 PDF.js2.2 window.open 直接打开2.3 弹框形式打开3 修改配置项3.1 修改主题色为暗色系3.2 修改默认语言为简...
    99+
    2022-12-24
    vue使用pdf.js vue3.0 pdf vue pdf 预览
  • SpringBoot+Vue3实现上传文件功能
    目录前言一、后端二、前端三、演示前言 开发后台系统时经常遇到实现上传文件的功能,在这记录一下我的方法 后端:SpringBoot2前端:Vue3+ElementPlus工具:IDEA...
    99+
    2023-01-28
    vue3 springboot 文件上传 vue3 springboot 上传
  • Springboot+Vue+axios实现文章收藏功能
    最近在做毕设,也是第一次使用前后分离框架我就边学边用springboot+vue做了一个博客文章的收藏功能,写得不好见谅,算是一个学习笔记吧,给大家分享一下,后面可能还会做一个关注/...
    99+
    2024-04-02
  • Spring Boot 项目中如何使用 Python 实现文件下载功能?
    随着互联网技术的不断发展,文件下载已经成为了现代应用程序中的必备功能之一。在 Spring Boot 项目中,我们可以使用多种语言来实现文件下载功能,其中 Python 也是一个不错的选择。本文将为大家介绍如何在 Spring Boot ...
    99+
    2023-09-04
    文件 关键字 spring
  • vue3+koa实现文件上传功能的全过程记录
    目录前言:技术引用:前端实现后台实现:引入koa-body,并注册中间件:引入koa-static 进行静态资源访问前台回显图片:遇到的问题:已解决:Token验证问题:proxy代...
    99+
    2023-01-04
    vue进行文件上传 vue3 koa文件上传 vue3 koa
  • 如何在PHP CMS系统中实现文章置顶功能
    在PHP CMS系统中,文章置顶功能是一种非常常见的需求。这种功能可以让网站管理员优先展示某些重要的文章,从而提高网站访问者的注意度。在本文中,我们将介绍如何在PHP CMS系统中实现...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作