Quill.js 富文本编辑器:通过自定义模块实现页面内目录导航 (TOC)


quill.js 富文本编辑器:通过自定义模块实现页面内目录导航 (toc)

本文详细介绍了如何在 Quill.js 富文本编辑器中,通过自定义其链接和标题模块,以实现自动生成页面内目录 (TOC) 的基础能力。核心在于修改链接默认行为以支持页面内锚点跳转,并为标题标签自动生成唯一 ID,从而为后续的目录生成奠定基础。

引言:Quill.js 与目录生成的需求

Quill.js 是一款功能强大的富文本编辑器,提供了丰富的编辑功能。然而,在某些场景下,例如长篇文章或技术文档,我们常常需要一个页面内目录 (Table of Contents, TOC) 来帮助用户快速导航。Quill.js 默认情况下并不直接支持自动生成 TOC,主要存在两个挑战:

  1. 链接默认行为:Quill.js 的默认链接模块通常会将所有链接设置为 target="_blank",这意味着点击链接会在新标签页中打开。这对于外部链接是理想的,但对于页面内的锚点链接,我们期望它们能在当前页面内平滑跳转。
  2. 标题标签缺乏唯一 ID:为了创建锚点链接,文档中的标题(如

    ,

    等)需要拥有唯一的 id 属性。Quill.js 默认生成的标题标签不包含这些 id 属性。

为了克服这些限制,我们需要对 Quill.js 的核心模块进行定制。

定制 Quill.js 链接模块

为了让页面内锚点链接能够在当前页面跳转,我们需要修改 Quill.js 链接模块的 target 属性行为。具体来说,当链接值以 # 开头时(表示一个页面内锚点),我们应该移除 target 属性或将其设置为 _self;对于其他外部链接,则保留 target="_blank"。

以下是定制链接模块的代码示例:

var Link = Quill.import('formats/link');

class MyLink extends Link {
    /**
     * 创建链接节点时的处理逻辑。
     * @param {string} value - 链接的值 (href)。
     * @returns {HTMLElement} - 创建的链接 DOM 节点。
     */
    static create(value) {
        let node = Link.create(value);
        value = Link.sanitize(value); // 清理链接值
        node.setAttribute('href', value);

        // 如果链接以 '#' 开头,则移除 target 属性,实现页面内跳转
        if (value.startsWith("#")) {
            node.removeAttribute('target');
        } else {
            // 否则,保持 target="_blank"
            node.setAttribute("target", "_blank");
        }
        return node;
    }

    /**
     * 格式化链接时的处理逻辑。
     * @param {string} name - 格式名称。
     * @param {string} value - 格式值。
     */
    format(name, value) {
        super.format(name, value);

        if (name !== this.statics.blotName || !value) {
            return;
        }

        // 再次检查并设置 target 属性,确保格式化后的行为正确
        if (value.startsWith("#")) {
            this.domNode.removeAttribute("target");
        } else {
            this.domNode.setAttribute("target", "_blank");
        }
    }
}

// 注册自定义的链接模块
Quill.register(MyLink, true); // 第二个参数为 true 表示覆盖默认模块

代码解析:

云从科技AI开放平台 云从科技AI开放平台

云从AI开放平台

云从科技AI开放平台 99 查看详情 云从科技AI开放平台
  • 我们通过 Quill.import('formats/link') 获取 Quill 默认的链接模块。
  • MyLink 类继承自 Link。
  • static create(value) 方法在创建新的链接 DOM 节点时被调用。我们在此处判断 value 是否以 # 开头来决定 target 属性。
  • format(name, value) 方法在链接被格式化(例如,用户编辑链接文本或 URL)时被调用。同样,我们在此处确保 target 属性的正确性。
  • Quill.register(MyLink, true) 将我们自定义的 MyLink 注册为 Quill 的链接模块,并覆盖了默认的实现。

定制 Quill.js 标题模块

为了让标题能够被锚点链接引用,每个标题标签都需要一个唯一的 id 属性。Quill.js 默认的标题模块并不会自动添加 id。我们需要扩展标题模块,在标题创建时为其分配一个唯一的 ID。

以下是定制标题模块的代码示例:

var Header = Quill.import('formats/header');
var ids = []; // 用于存储已生成的ID,确保唯一性

/**
 * 生成一个简单的随机ID。
 * 实际应用中可能需要更健壮的ID生成策略。
 * @returns {string} - 唯一的ID字符串。
 */
function getRandomId() {
    let _id = Math.random().toString(16).slice(2, 9);
    // 简单检查冲突,实际应用中可能需要更复杂的循环或UUID库
    while(ids.includes(_id)) {
        _id = Math.random().toString(16).slice(2, 9);
    }
    ids.push(_id);
    return _id;
}

class MyHeader extends Header {
    /**
     * 构造函数在标题 DOM 节点被创建并附加到文档时调用。
     * @param {HTMLElement} domNode - 标题的 DOM 节点。
     */
    constructor(domNode) {
        super(domNode);
        // 为标题节点设置唯一的ID
        domNode.setAttribute('id', getRandomId());
        this.cache = {}; // Quill 内部使用,保持一致
    }

    /**
     * 静态方法 create() 用于创建新的 Blot 实例。
     * 这里我们保持与父类一致,因为ID的设置主要在 constructor 中处理。
     * @returns {HTMLElement} - 创建的标题 DOM 节点。
     */
    static create() {
        const node = super.create();
        return node;
    }

    /**
     * 静态方法 formats() 用于获取 Blot 的格式属性。
     * 我们添加 id 属性以便于后续获取。
     * @param {HTMLElement} domNode - 标题的 DOM 节点。
     * @returns {object} - 包含 id 属性的对象。
     */
    static formats(domNode) {
        let formats = super.formats(domNode);
        formats.id = domNode.getAttribute("id");
        return formats;
    }
}

// 注册自定义的标题模块
Quill.register("formats/header", MyHeader, true);
// 确保 blotName 正确,Quill 内部使用
MyHeader.blotName = "header";

代码解析:

  • getRandomId() 函数用于生成一个随机字符串作为 ID。在实际生产环境中,建议使用更健壮的 UUID 或基于内容的哈希值来生成 ID,以避免潜在的冲突和提高可读性。
  • MyHeader 类继承自 Header。
  • constructor(domNode) 是关键。当 Quill 创建一个标题 DOM 节点时,会调用此构造函数。我们在这里调用 domNode.setAttribute('id', getRandomId()) 来为该标题节点设置一个唯一的 ID。
  • static formats(domNode) 方法被重写,以便在获取标题的格式信息时,也能包含其 id 属性。这在后续遍历 Quill 内容时获取标题 ID 会很有用。
  • Quill.register("formats/header", MyHeader, true) 将我们自定义的 MyHeader 注册为 Quill 的标题模块,并覆盖了默认实现。

实现目录生成逻辑

通过上述定制,我们的 Quill.js 编辑器现在具备了以下能力:

  1. 内部链接支持:当用户在编辑器中创建以 # 开头的链接时,它们将作为页面内锚点链接,并在当前页面跳转。
  2. 标题唯一 ID:所有通过 Quill.js 创建的标题(h1 到 h6)都将自动拥有一个唯一的 id 属性。

有了这些基础,接下来就是编写实际的 J*aScript 代码来生成目录。这通常涉及以下步骤:

  1. 获取编辑器内容:可以通过 quill.getContents() 获取 Quill 内部的 Delta 格式内容,或者直接访问编辑器 DOM (quill.root)。

  2. 遍历并提取标题

    • 如果使用 Delta 格式,需要遍历 Delta 操作,识别出 header 格式的文本,并从对应的 DOM 节点中提取其 id 和文本内容。
    • 如果直接访问 DOM,可以使用 document.querySelectorAll('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]') 来获取所有带有 id 属性的标题元素。
  3. 构建目录结构:根据提取到的标题文本和 ID,动态生成一个 HTML 列表(通常是

    • 嵌套 标签)作为目录。例如:
      <n* class="toc">
          <ul>
              <li><a href="#header-id-1">一级标题内容</a></li>
              <li>
                  <ul>
                      <li><a href="#header-id-2">二级标题内容</a></li>
                  </ul>
              </li>
          </ul>
      </n*>
    • 插入目录:将生成的目录 HTML 结构插入到页面的指定位置,例如文章内容的顶部。

示例(概念性代码,不直接包含在 Quill 定制中):

// 假设 quill 实例已经初始化
function generateTableOfContents(quill) {
    const editorContent = quill.root; // 获取编辑器内容的 DOM 元素
    const headers = editorContent.querySelectorAll('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]');

    if (headers.length === 0) {
        return ''; // 没有标题,不生成目录
    }

    let tocHtml = '<ul>';
    let currentLevel = 0; // 跟踪当前标题级别

    headers.forEach(header => {
        const level = parseInt(header.tagName.substring(1)); // 获取标题级别 (h1 -> 1, h2 -> 2)
        const id = header.getAttribute('id');
        const text = header.textContent;

        if (level > currentLevel) {
            // 新的子级别,开始新的无序列表
            for (let i = 0; i < (level - currentLevel); i++) {
                tocHtml += '<ul>';
            }
        } else if (level < currentLevel) {
            // 返回上级,关闭多余的无序列表
            for (let i = 0; i < (currentLevel - level); i++) {
                tocHtml += '</ul>';
            }
        }

        tocHtml += `<li><a href="#${id}">${text}</a></li>`;
        currentLevel = level;
    });

    // 关闭所有未闭合的无序列表
    for (let i = 0; i < currentLevel; i++) {
        tocHtml += '</ul>';
    }

    return tocHtml;
}

// 在页面加载或编辑器内容更新后调用
// const tocContainer = document.getElementById('toc-container');
// if (tocContainer) {
//     tocContainer.innerHTML = generateTableOfContents(quill);
// }

注意事项与总结

  • ID 生成策略:示例中使用了 Math.random() 生成 ID,这在小型应用中可能够用,但在大型或高并发环境中,可能需要更鲁棒的 UUID (Universally Unique Identifier) 库来保证 ID 的全球唯一性。
  • 性能优化:如果文章内容非常庞大,频繁地遍历 DOM 或 Delta 内容来生成 TOC 可能会影响性能。可以考虑在内容更新时进行防抖 (debounce) 或节流 (throttle) 处理,或者仅在需要时(例如用户点击“显示目录”按钮)才生成。
  • 样式与交互:生成的目录通常需要 CSS 样式来美化,并且可以添加 J*aScript 交互,例如点击目录项时平滑滚动到对应位置,或高亮当前视口中的标题。
  • Quill 版本兼容性:Quill.js 的 API 在不同版本之间可能存在细微差异。请确保你的代码与所使用的 Quill.js 版本兼容。
  • 更复杂的目录结构:上述示例生成的是一个简单的嵌套列表。如果需要更复杂的目录结构(例如,在目录中显示标题编号),则需要在 generateTableOfContents 函数中加入额外的逻辑。

通过对 Quill.js 的链接和标题模块进行定制,我们成功解决了在编辑器中实现页面内目录导航的关键障碍。这展示了 Quill.js 强大的可扩展性,允许开发者根据特定需求深度定制其行为。在此基础上,结合额外的 J*aScript 逻辑来解析编辑器内容并动态渲染目录,即可实现一个完整的 Quill.js 自动目录生成功能。

以上就是Quill.js 富文本编辑器:通过自定义模块实现页面内目录导航 (TOC)的详细内容,更多请关注其它相关文章!


# 器中  # 新浪网站的软文推广费用  # 上海seo教程的好方法  # 眉山百度知识营销推广公司  # 展会推广营销词  # su优化模型网站  # 怎么查看seo分析  # 备课数学网站建设  # 无线店铺的营销推广策略  # 如何做自己的网站推广呢  # 拉萨seo网站优化软件  # 设置为  # 这在  # 文档  # css  # 自动生成  # 遍历  # 跳转  # 自定义  # 页面内  # 编辑器  # ai  # node  # js  # html  # java  # javascript 


相关栏目: 【 Google疑问12 】 【 Facebook疑问10 】 【 优化推广96088 】 【 技术知识133117 】 【 IDC资讯59369 】 【 网络运营7196 】 【 IT资讯61894


相关推荐: 易车网官网直达入口 易车网在线登录入口  word页码灰色不能用如何解决  三星M34录音变声问题_Samsung M34麦克风调整  三星A55应用闪退排查步骤_Samsung A55稳定性优化技巧  《书耽》更换手机号方法  《磁力猫》最好用的磁官网  飞飞漫画漫画阅读官网_飞飞漫画漫画阅读官网进入阅读  高效调试PHP大型嵌套数组:JSON序列化与可视化工具实践  Eclipse开发J*a快速入门  使用CSS :has() 选择器实现父元素样式控制:从子元素反向应用样式  《波斯王子:失落的王冠》剑术大师打法攻略  电脑没有声音了怎么办 电脑声音问题的全面排查与修复指南【详解】  mysql如何回滚事务_mysql ROLLBACK事务回滚方法  怎样设置开机后自动运行某个程序_Windows启动文件夹与任务计划【自动化】  Python定时发送QQ消息  CSS如何控制元素外边距_margin实现布局间隔  消除网页顶部意外空白线:CSS布局常见问题与解决方案  智慧职教mooc平台登录网址 智慧职教mooc官网直达  猫眼电影app如何筛选支持退改签的影院_猫眼电影退改签影院筛选方法  win11怎么启用或禁用休眠 Win11 powercfg命令管理休眠文件【技巧】  创客贴登录页面入口 创客贴网页版最新网址链接  如何通过settings.json个性化您的VS Code体验  C#解析并修改XML后保存 如何确保格式与编码的正确性  芒果TV官网登录入口 芒果TV官方网站登录入口  ao3入口镜像地址 ao3镜像入口可靠跳转  天天漫画2025最新入口 天天漫画永久有效登录入口  支付宝网页版在线入口 支付宝官网电脑登录入口  《知到》打卡课程方法  Teambition网盘如何共享文件  鲨鱼剧场app金币获取方法  漫蛙漫画直连入口 _ manwa官方备用入口实时检测  CSS如何在页面中引入重置样式_使用Normalize.css或Reset.css统一浏览器默认样式  漫蛙漫画官方网站使用_漫蛙manwa网页版在线入口教程  《下一站江湖2》武器获取方法  J*aScript字符串_Unicode处理  使用jQuery精确检测除指定元素外任意位置的点击事件  嘀嗒顺风车如何开具电子发票  《七读免费小说》开通会员方法  《地下城堡4:骑士与破碎编年史》墓穴挑战125攻略  263企业邮箱如何设置邮件转发功能  食品生产用水只要符合国家规定的生活饮用水卫生标准就可以吗  J*a中的值传递到底指什么_值传递模型在参数传递中的真正含义说明  在J*a中如何实现在线问答与评分系统_问答评分项目开发方法说明  C++ optional用法详解_C++17处理可能为空的返回值  b站网页版入口 哔哩哔哩官方网站直接进入  基于键值条件高效映射 Pandas DataFrame 多列数据  家里的小飞虫总是不断,用什么方法可以彻底根除?  管理打开的编辑器:固定、分组和关闭技巧  优化 React onClick 事件处理:函数引用与箭头函数的对比  在PySimpleGUI中实现键盘按键绑定按钮事件 

 2025-10-14

了解您产品搜索量及市场趋势,制定营销计划

同行竞争及网站分析保障您的广告效果

点击免费数据支持

提交您的需求,1小时内享受我们的专业解答。

运城市盐湖区信雨科技有限公司


运城市盐湖区信雨科技有限公司

运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。

 8156699

 13765294890

 8156699@qq.com

Notice

We and selected third parties use cookies or similar technologies for technical purposes and, with your consent, for other purposes as specified in the cookie policy.
You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice or by continuing to browse otherwise.