ck-editor5的研究 (3):初步使用 CKEditor5 的事件系统和API

news2025/6/3 17:09:36

前言

在上一篇文章中—— ck-editor5的研究(2):对 CKEditor5 进行设计,并封装成一个可用的 vue 组件 ,我已经把 CKEditor5 封装成了一个通用vue组件,并且成功在nuxt中运行,并具备一定的通用性,已经可以正式使用了。

但是,它只能完全替换内容,或者手动输入,并不能通过使用 js 的方法,对 CKEditor5 进行更细粒度的控制,比如 在编辑器中插入一小部分内容

那么这篇文章,我将初步研究 CKEditor5 的 事件系统API。大概的效果如下:
在这里插入图片描述

大概分成了3步

1. 理解CKEditor5的核心概念

首先,来看看这张图:

在这里插入图片描述
也可以去 官方文档 看,但是 ckeditor 设计理念太过于庞大,太难读懂了。我这里就按我的理解,把CKEditor5比作人体的 脑(Model)眼睛(Input data)手和脚(Editing view 和 Data view) 三大部分了。

注意:这里我们换了个称呼,把 Output data 叫成 Data view。方便后面写代码更容易理解API。

于是我们就按照常理思考:眼睛用于接收信息,通过神经再次转化信息,并传入我们的大脑,大脑经过思考后下达命令给手和脚,让手和脚都握笔写字 (做同一件事情)。

相对应地,CKEditor5也一样:编辑区域接收用户输入的内容(Input data),通过捕捉并向上转化(Data upcast),传入Model中,Model再把数据进行向下转化(downcast),处理成一条条的命令(也就是给一个对象添加一个个属性),并且分配到Editing 对象和 Data 对象中去,等待我们进行调用。

我们先打印一下 editor实例对象,进行观察:
编辑器实例对象
我们发现有 model、editing、data、conversion,正好对应上面提到的 Model、Editing view、Data view 和转化器(upcast, downcast)。

尝试理解一下官方的架构图(MVC设计模式),找找对应的部位:
在这里插入图片描述

2. 开始搭建目录

仍然使用之前的代码,准备 一个 ts ,一个 vue组件,一个 demo3/index.vue 测试页面。我将会重点在 demo3/index.vue 里面写代码,先观察一下3个文件大致内容:
在这里插入图片描述
在这里插入图片描述在这里插入图片描述

3. 尝试做些事情

通过观察,可以看到,我已经在组件中用 @ready 事件把编辑器实例对象传送了出来,并且用 editorInstance 进行了接收,这样我们就可以在页面中调用实例对象的方法了。

1. 点击按钮插入文本到末尾

我们添加一个按钮和事件,点击按钮时,插入文本到末尾:

/**
 * 点击按钮插入文本到末尾
 */
function insertTextToEnd() {
  if (!editorInstance) {
    return;
  }
  /**
   * 使用model.change方法,可以改写编辑内容, change方法是一个回调函数,参数是writer,
   * 在这里找它的属性 https://ckeditor.com/docs/ckeditor5/latest/api/module_engine_model_writer-Writer.html
   * 这是我们第一篇文章提到过的 API 文档
   */
  editorInstance.model.change((writer) => {
    /**
     * 找到根节点<root> (可以理解为dom树的根节点 document.documentElement 也就是 <html> 标签),
     * ckeditor5也有自己的一棵dom树,但与网页文档对象dom树不一样,而是一一对应的
     */
    const root = editorInstance?.model.document.getRoot();
    if (!root) {
      return;
    }
    // 获取末尾位置
    const endPosition = writer.createPositionAt(root, 'end');
    console.log('endPosition :>> ', endPosition);
    // 移动光标到末尾
    writer.setSelection(endPosition);
    // 执行enter命令
    editorInstance?.execute('enter');

    // 插入文本
    editorInstance?.model.change((writer) => {
      const content = writer.createText('The End!');
      console.log('content :>> ', content);
      editorInstance?.model.insertContent(content);
    });
  });
}

看下效果,注意看我的光标:
在这里插入图片描述

2. 添加内容变更事件

// 内容变更事件
const initContentChangeEvent = () => {
  editorInstance?.model.document.on('change:data', () => {
    console.log('The data has changed!');
  });
};

3. 修改默认回车事件

默认回车换行,是创建一个 p 标签,我们使用 Editing view,把它换成 br 标签。
默认的 shitf + 回车 是创建一个 br 标签,反而变成 p 标签了。

/**
 * 把默认的回车事件,由 p 标签改为 br 标签
 */
function changeEnterEvent() {
  editorInstance?.editing.view.document.on(
    'enter',
    (evt, data) => {
      data.preventDefault();
      evt.stop();

      if (data.isSoft) {
        editorInstance?.execute('enter');
        editorInstance?.editing.view.scrollToTheSelection();
        return;
      }

      editorInstance?.execute('shiftEnter');
      editorInstance?.editing.view.scrollToTheSelection();
    },
    { priority: 'high' },
  );
}

4. 插入一些较长的 HTML 代码

使用 Data view 方式来插入内容

/**
 * 插入一些较长的 HTML 代码
 */
function insertLongHtmlCode() {
  editorInstance?.model.change((writer) => {
    const content = '<p>A paragraph with <a href="https://ckeditor.com">some link</a>.</p>';
    const viewFragment = editorInstance?.data.processor.toView(content);
    if (!viewFragment) {
      return;
    }
    const modelFragment = editorInstance?.data.toModel(viewFragment);
    if (!modelFragment) {
      return;
    }
    editorInstance?.model.insertContent(modelFragment);
  });
}

最终的demo代码和测试效果图

demo的代码

<template>
  <div class="space-y-4">
    <h1 class="text-xl font-bold">demo3: 初步使用ckeditor5的 事件 和 API</h1>
    <!-- TODO 测试控制面板 -->
    <div class="flex gap-2">
      <button
        @click="insertTextToEnd"
        class="text-white cursor-pointer rounded border-none bg-blue-500 px-4 py-2 text-[#fff] outline-none">
        插入文本到末尾
      </button>
      <button
        @click="changeEnterEvent"
        class="text-white cursor-pointer rounded border-none bg-blue-500 px-4 py-2 text-[#fff] outline-none">
        把默认的回车事件,由 p 标签改为 br 标签
      </button>
      <button
        @click="insertLongHtmlCode"
        class="text-white cursor-pointer rounded border-none bg-blue-500 px-4 py-2 text-[#fff] outline-none">
        插入一些较长的 HTML 代码
      </button>
    </div>
    <!-- 编辑器组件 -->
    <ClientOnly>
      <ck-editor3 @ready="onEditorReady" />
    </ClientOnly>
  </div>
</template>

<script setup lang="ts">
import type MyClassicEditor from '@/components/ck/editor3/ckeditor3';

let editorInstance: MyClassicEditor | null = null;
const onEditorReady = (editor: MyClassicEditor) => {
  editorInstance = editor;
  console.log('editorInstance :>> ', editorInstance);
  initContentChangeEvent();
};

/**
 * 点击按钮插入文本到末尾
 */
function insertTextToEnd() {
  /**
   * 使用model.change方法,可以改写编辑内容, change方法是一个回调函数,参数是writer,
   * 在这里找它的属性 https://ckeditor.com/docs/ckeditor5/latest/api/module_engine_model_writer-Writer.html
   * 这是我们第一篇文章提到过的 API 文档
   */
  editorInstance?.model.change((writer) => {
    /**
     * 找到根节点<root> (可以理解为dom树的根节点 document.documentElement 也就是 <html> 标签),
     * ckeditor5也有自己的一棵dom树,但与网页文档对象dom树不一样,而是一一对应的
     */
    const root = editorInstance?.model.document.getRoot();
    if (!root) {
      return;
    }
    // 获取末尾位置
    const endPosition = writer.createPositionAt(root, 'end');
    // console.log('endPosition :>> ', endPosition);
    // 移动光标到末尾
    writer.setSelection(endPosition);
    // 执行enter命令
    editorInstance?.execute('enter');

    // 插入文本
    editorInstance?.model.change((writer) => {
      const content = writer.createText('The End!');
      // console.log('content :>> ', content);
      editorInstance?.model.insertContent(content);
    });
  });
}

// 内容变更事件
const initContentChangeEvent = () => {
  editorInstance?.model.document.on('change:data', () => {
    console.log('The data has changed!');
  });
};

/**
 * 把默认的回车事件,由 p 标签改为 br 标签
 */
function changeEnterEvent() {
  editorInstance?.editing.view.document.on(
    'enter',
    (evt, data) => {
      data.preventDefault();
      evt.stop();

      if (data.isSoft) {
        editorInstance?.execute('enter');
        editorInstance?.editing.view.scrollToTheSelection();
        return;
      }

      editorInstance?.execute('shiftEnter');
      editorInstance?.editing.view.scrollToTheSelection();
    },
    { priority: 'high' },
  );
}

/**
 * 插入一些较长的 HTML 代码
 */
function insertLongHtmlCode() {
  editorInstance?.model.change((writer) => {
    const content = '<p>A paragraph with <a href="https://ckeditor.com">some link</a>.</p>';
    const viewFragment = editorInstance?.data.processor.toView(content);
    if (!viewFragment) {
      return;
    }
    const modelFragment = editorInstance?.data.toModel(viewFragment);
    if (!modelFragment) {
      return;
    }
    editorInstance?.model.insertContent(modelFragment);
  });
}
</script>

<style lang="less" scoped>
// 样式
</style>

效果图

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

WPS快速排版

论文包括&#xff08;按顺序&#xff09;&#xff1a;封面&#xff08;含题目&#xff09;、摘 要、关键词、Abstract&#xff08;英文摘要&#xff09;、Keywords、目录、正文、参考文献、在读期间发表的学术论文及研究成果&#xff0c;致 谢 题目&#xff08;黑小一加粗&…

Java实现命令行图书管理系统(附完整源码)

一、项目概述 本文将介绍如何使用Java实现一个基于命令行的图书管理系统。系统支持管理员和普通用户两种角色&#xff0c;提供图书的增删改查、借阅归还等功能。项目采用面向对象设计原则&#xff0c;代码结构清晰&#xff0c;适合Java初学者学习。 二、系统功能架构 graph T…

【数据结构】顺序表和链表详解(上)

前言&#xff1a;上期我们介绍了算法的复杂度&#xff0c;知道的算法的重要性同时也了解到了评判一个算法的好与坏就去看他的复杂度(主要看时间复杂度)&#xff0c;这一期我们就从顺序表和链表开始讲起。 文章目录 一&#xff0c;顺序表1&#xff0c;线性表2&#xff0c;顺序表…

唯创WT2606B TFT显示灵动方案,重构电子锁人机互动界面,赋能智能门锁全场景交互!

在智能家居的浪潮中&#xff0c;门锁搭载显示屏已成为行业创新的焦点。据行业数据显示&#xff0c;2023年全球智能门锁出货量中&#xff0c;搭载显示屏的型号占比已突破40%&#xff0c;且年复合增长率达25%。而2024年国内智能门锁销量突破2200万套&#xff0c;预计2025年市场规…

智能穿戴新标杆:SD NAND (贴片式SD卡)与 SOC 如何定义 AI 眼镜未来技术路径

目录 一、SD NAND&#xff1a;智能眼镜的“记忆中枢”突破空间限制的存储革命性能与可靠性的双重保障 二、SOC芯片&#xff1a;AI眼镜的“智慧大脑”从性能到能效的全面跃升多模态交互的底层支撑 三、SD NANDSOC&#xff1a;11&#xff1e;2的协同效应数据流水线的高效协同端侧…

node_modules包下载不下来

如果项目里面的package-lock.json有resolved &#xff0c;就指向了包的下载来源&#xff0c;如果这个网址挂了&#xff0c;那npm i 就会一直卡着。而且&#xff0c;在终端去修改 npm的镜像是没有用的 解决办法是:把项目里面的 lock文件 .npmrc都删了 然后重新下载就可以了

yolo个人深入理解

卷积层的理解,通过云端服务器训练模型,模型构建的重要性,针对极低像素的处理,模型训练召回率提高技巧,卷积层2,4,8,16,32的小模型与大模型的理解 一.关于backbone,neck,head深入理解 1,backbone的主要组成部分是sppf和conv,这是backbone的核心,其中yolov5和yolov8…

从0开始学vue:Element Plus详解

一、核心架构解析二、技术实现指南三、高级特性实现四、性能优化方案五、生态扩展方案六、调试与测试七、版本演进路线 Element Plus 是专为 Vue 3 设计的桌面端 UI 组件库&#xff0c;基于 Vue 3 的 Composition API 重构&#xff0c;在保持与 Element UI 兼容性的同时&#x…

互联网向左,区块链向右

2008年&#xff0c;中本聪首次提出了比特币的设想&#xff0c;这打开了去中心化的大门。 比特币白皮书清晰的描述了去中心化支付的解决方案&#xff0c;并分别从以下几个方面阐述了他的理念&#xff1a; 一、由转账双方点对点的通讯&#xff0c;而不通过中心化的第三方&#xf…

Python6.1打卡(day33)

DAY 33 MLP神经网络的训练 知识点回顾&#xff1a; 1.PyTorch和cuda的安装 2.查看显卡信息的命令行命令&#xff08;cmd中使用&#xff09; 3.cuda的检查 4.简单神经网络的流程 1.数据预处理&#xff08;归一化、转换成张量&#xff09; 2.模型的定义 …

论文阅读笔记——Quo Vadis, Action Recognition? A New Model and the Kinetics Dataset

I3D 论文 UCF-101&#xff08;13000多个视频&#xff09;和 HMDB-51&#xff08;7000多个视频&#xff09;数据集过小&#xff0c;提出了 Kinetics 数据集&#xff0c;并且在其之上预训练之后能够迁移到其他小的数据集。 2DLSTM&#xff1a;使用2D CNN的好处是可以直接从 Ima…

vscode编辑器怎么使用提高开发uVision 项目的效率,如何编译Keil MDK项目?

用vscode编译uVision 项目只需要安装一个Keil Assistant插件&#xff0c;即可用vscode开发“keil 项目”。极大提高开发速度&#xff01; 1.安装Keil Assistant插件 安装插件成功之后&#xff0c;应该会让安装一个东西&#xff0c;点击安装即可 2.配置安装包路径 3.打开 uVi…

AR测量工具:精准测量,多功能集成

在日常生活中&#xff0c;我们常常会遇到需要测量物体长度、距离或角度的情况。无论是装修房屋、制作家具&#xff0c;还是进行户外活动&#xff0c;一个精准的测量工具都能大大提高我们的工作效率。AR测量工具就是这样一款集多种功能于一体的实用测量软件&#xff0c;它利用增…

【Go-补充】Sync包

并发编程-Sync包 sync.WaitGroup 在代码中生硬的使用time.Sleep肯定是不合适的&#xff0c;Go语言中可以使用sync.WaitGroup来实现并发任务的同步。 sync.WaitGroup有以下几个方法&#xff1a; 方法名功能(wg * WaitGroup) Add(delta int)计数器delta(wg *WaitGroup) Done()…

设备驱动与文件系统:01 I/O与显示器

操作系统设备驱动学习之旅——以显示器驱动为例 从这一节开始&#xff0c;我要学习操作系统的第四个部分&#xff0c;就是i o设备的驱动。今天要讲的是第26讲&#xff0c;内容围绕i o设备中的显示器展开&#xff0c;探究显示器是如何被驱动的&#xff0c;也就是操作系统怎样让…

智慧充电桩数字化管理平台:环境监测与动态数据可视化技术有哪些作用?

随着新能源汽车的普及&#xff0c;智慧充电桩作为基础设施的重要组成部分&#xff0c;正逐步向数字化、智能化方向发展。环境监测与动态数据可视化技术的应用&#xff0c;为充电桩的高效管理和运维提供了全新解决方案。通过实时采集环境参数与运行数据&#xff0c;并结合可视化…

家政小程序开发,开启便捷生活新篇章

在快节奏的现代生活中&#xff0c;家务琐事常常让人分身乏术&#xff0c;如何高效解决家政服务需求成了众多家庭的难题。家政小程序开发&#xff0c;正是为解决这一痛点而生&#xff0c;它将为您带来前所未有的便捷生活体验。 想象一下&#xff0c;您只需打开手机上的家政小程…

李臻20242817_安全文件传输系统项目报告_第14周

安全文件传输系统项目报告&#xff08;第 14 周&#xff09; 1. 代码链接 Gitee 仓库地址&#xff1a;https://gitee.com/li-zhen1215/homework/tree/master/Secure-file 代码结构说明&#xff1a; SecureFileTransfer/ ├── client/ # 客户端主目…

20250531MATLAB三维绘图

MATLAB三维绘图 三维曲线&#xff1a;plot3功能介绍代码实现过程plot3实现效果 三维曲面空间曲面作图命令&#xff1a;meshmeshgrid语法示例应用meshgrid实操训练 peakspeaks 的基本用法peaks数学表达式实操训练自定义网格大小使用自定义网格 meshMATLAB代码对齐快捷键Ctrli墨西…

深入理解C#异步编程:原理、实践与最佳方案

在现代软件开发中&#xff0c;应用程序的性能和响应能力至关重要。特别是在处理I/O密集型操作&#xff08;如网络请求、文件读写、数据库查询&#xff09;时&#xff0c;传统的同步编程方式会导致线程阻塞&#xff0c;降低程序的吞吐量。C# 的异步编程模型&#xff08;async/aw…