ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

CKEditor 5 剪贴板功能解析:复制、剪切、粘贴与拖拽管线实现指南

CKEditor 5 剪贴板功能解析:复制、剪切、粘贴与拖拽管线实现指南 CKEditor 5 剪贴板功能解析复制、剪切、粘贴与拖拽管线实现指南【免费下载链接】ckeditor5Powerful rich text editor framework with a modular architecture, modern integrations, and features like collaborative editing.项目地址: https://gitcode.com/GitHub_Trending/ck/ckeditor5ckeditor/ckeditor5-clipboard是 CKEditor 5 中负责剪贴板复制、剪切、粘贴与拖拽drag drop支持的官方功能包。本文将基于该包在 ckeditor5 仓库 中的 README、框架深潜文档 与源码实现完整讲解其输入/输出事件管线、纯文本粘贴PastePlainText与拖拽DragDrop两大子特性并给出基于事件 API 的自定义扩展实战代码帮助你在自己的集成中精确控制粘贴进编辑器与从编辑器复制出去的内容。包定位与安装该包实现了 CKEditor 5 的剪贴板复制、剪切、粘贴支持是官方 glue胶水插件Clipboard的载体。包 README 明确指出它属于ckeditor5聚合包的一部分安装整个聚合包即可使用npm install ckeditor5安装后在插件列表中加入Clipboard以及随编辑器一起加载的Essentials等基础插件import { ClassicEditor, Clipboard, Bold } from ckeditor5; ClassicEditor .create( { licenseKey: YOUR_LICENSE_KEY, // 或填写 GPL plugins: [ Clipboard, Bold, /* ... */ ], } ) .then( /* ... */ ) .catch( /* ... */ );从源码结构看Clipboard是一个典型的聚合插件在 src/clipboard.ts 中其requires声明了四个子插件加载Clipboard会自动激活它们ClipboardPipeline—— 剪贴板输入/输出事件管线src/clipboardpipeline.tsDragDrop—— 编辑器内部与编辑器内外的拖拽src/dragdrop.tsPastePlainText——Ctrl/Cmd Shift V纯文本粘贴src/pasteplaintext.tsClipboardMarkersUtils—— 配合评论、修订等协作功能复制/粘贴模型标记的工具类。此外API 文档 说明该插件通常由Essentials插件启用因此在包含Essentials的标准集成中无需手动添加。核心设计思想接管原生剪贴板事件剪贴板功能由 Clipboard 插件 实现负责与操作系统和浏览器提供的原生剪贴板集成覆盖用户在编辑器内复制、剪切、粘贴以及拖拽内容的场景。关键设计原则是CKEditor 5 拦截所有原生copy、cut、drop事件并在自己的事件系统中处理绝不让浏览器直接触碰富文本内容——因为浏览器默认行为会破坏编辑器内部的格式化与结构。因此存在两个方向的内容处理流程内容被粘贴或拖入编辑器时走输入管线Input pipeline内容被复制、剪切或拖出编辑器时走输出管线Output pipeline。两条管线都允许各功能插件在多个阶段处理待插入或待写入剪贴板的内容也允许在任意阶段覆盖默认机制。完整的事件细节见 框架深潜文档 与 clipboardpipeline.ts 顶部注释。输入管线粘贴与拖入的五步流程当用户向编辑器粘贴或拖入内容时浏览器触发原生事件剪贴板功能拦截后依次执行以下步骤详见 deep-dive/clipboard.mdClipboardObserver转换事件ClipboardObserver 将原生事件转换为合成事件view.Document#paste或view.Document#drop归一为clipboardInput粘贴与拖入的后续处理路径基本一致因此两个事件被统一为单个view.Document#clipboardInput事件便于统一处理检索并预处理数据剪贴板功能监听clipboardInput从事件data.dataTransfer中读取text/html或text/plain内容读取逻辑见 clipboardobserver.ts优先text/html缺失时回退text/plain并调用 plaintexttohtml.ts 转换随后触发ClipboardPipeline#inputTransformation事件内容以view.DocumentFragment形式放在data.content中视图转模型监听inputTransformation的默认处理器把视图文档片段转换为model.DocumentFragment再触发ClipboardPipeline#contentInsertion事件此步骤使用$clipboardHolder作为全允许转换上下文见 clipboardpipeline.ts且contentInsertion在单个模型 change 块内触发以避免插入过程中被选择位置 post-fixer 等中间打断插入内容监听contentInsertion的默认处理器调用model.insertContent()将模型片段插入编辑器并把包含所有变更的范围存入事件数据的resultRange。输入管线的完整事件流如下示意图出自 deep-dive/clipboard.md┌──────────────────────┐ ┌──────────────────────┐ │ view.Document │ │ view.Document │ │ paste │ │ drop │ └───────────┬──────────┘ └───────────┬──────────┘ │ │ └────────────────┌────────────────┘ │ ┌─────────V────────┐ │ view.Document │ Retrieves text/html from data.dataTransfer │ clipboardInput │ and processes it to view.DocumentFragment. └─────────┬────────┘ │ ┌───────────V───────────┐ │ ClipboardPipeline │ Converts view.DocumentFragment │ inputTransformation │ to model.DocumentFragment. └───────────┬───────────┘ │ ┌──────────V──────────┐ │ ClipboardPipeline │ Calls model.insertContent(). │ contentInsertion │ └─────────────────────┘事件优先级与覆盖机制剪贴板功能对view.Document#clipboardInput、ClipboardPipeline#inputTransformation、ClipboardPipeline#contentInsertion这三个事件均使用低优先级监听器源码中统一为{ priority: low }见 clipboardpipeline.ts。这意味着添加一个普通优先级的监听器并调用evt.stop()就能覆盖剪贴板功能的默认行为——机制上与 DOM 中用evt.preventDefault()覆盖浏览器默认行为类似。另外在输入路径上还有一个默认保护clipboardpipeline.ts 以highest优先级监听clipboardInput当data.method paste且当前选择位于不可编辑区域!editor.model.canEditAt( ... )时直接evt.stop()从而禁止在只读/受保护区域粘贴剪切操作在 src/clipboardpipeline.ts 中有同样的只读保护。自定义剪贴板输入读取其他数据类型默认情况下剪贴板功能从剪贴板读取text/html或text/plain做少量归一化例如清理空白处理相关的问题见 normalizeclipboarddata.ts再转成view.DocumentFragment并触发inputTransformation。你可以利用view.Document#clipboardInput事件覆盖这一默认行为典型场景有两个场景一处理粘贴或拖入的文件。文件可以从dataTransfer中读取。不过完整实现文件上传远比读取data.dataTransfer.files复杂官方建议直接参考图片上传插件的源码ImageUploadEditing位于 packages/ckeditor5-image/src/imageupload/imageuploadediting.ts。场景二改变剪贴板功能读取的数据类型。例如当dataTransfer中存在application/rtf时优先使用它忽略text/htmleditor.editing.view.document.on( clipboardInput, ( evt, data ) { const dataTransfer data.dataTransfer; const rtfContent dataTransfer.getData( application/rtf ); // 如果没有 RTF 内容中止并让剪贴板功能按默认逻辑处理。 if ( !rtfContent ) { return; } // 将 RTF 原始字符串转换为视图文档片段。 const viewContent convertRtfStringToView( rtfContent ); // 把视图片段交给默认的剪贴板输入处理器继续后续处理。 data.content viewContent; } );处理待插入内容inputTransformation 扩展实战ClipboardPipeline#inputTransformation事件让你可以在内容真正插入编辑器之前修改它。默认行为是触发contentInsertion事件当数据非空时将data.content表示的view.DocumentFragment插入编辑器。一个经典例子实现粘贴纯文本 URL 自动转为链接的功能。在inputTransformation阶段判断内容是否为单个 URL 文本若是则用ViewUpcastWriter把它重写为带href的a元素const writer new ViewUpcastWriter( editor.editing.view.document ); editor.plugins.get( ClipboardPipeline ).on( inputTransformation, ( evt, data ) { if ( data.content.childCount 1 isUrlText( data.content.getChild( 0 ) ) ) { const linkUrl data.content.getChild( 0 ).data; data.content writer.createDocumentFragment( [ writer.createElement( a, { href: linkUrl }, [ writer.createText( linkUrl ) ] ) ] ); } } );注意inputTransformation的事件文档特别强调不要通过evt.stop()来改变输入数据而应直接修改data.content属性见 clipboardpipeline.ts。由于把内容插入编辑器这一默认动作由低优先级监听器完成普通监听器可以覆盖它若希望在内容已插入之后再执行后续动作则使用lowest优先级editor.plugins.get( ClipboardPipeline ).on( contentInsertion, ( evt, data ) { console.log( Content was inserted. ); }, { priority: lowest } );关于事件监听器优先级highest/high/normal/low/lowest的完整说明可参考框架的事件系统深潜文档docs/framework/architecture 相关文档。输出管线复制与剪切的三个环节输出管线是输入管线的镜像处理复制copy与剪切cut操作允许处理将被写入剪贴板的内容也允许覆盖整个过程。其事件流如下示意图出自 deep-dive/clipboard.md┌──────────────────────┐ ┌──────────────────────┐ Retrieves the selected │ view.Document │ │ view.Document │ model.DocumentFragment │ copy │ │ cut │ and fires the outputTransformation └───────────┬──────────┘ └───────────┬──────────┘ event. │ │ └────────────────┌────────────────┘ │ ┌─────────────V────────────┐ Processes model.DocumentFragment │ ClipboardPipeline │ and converts it to │ outputTransformation │ view.DocumentFragment. └──────────────────────────┘ │ ┌─────────────V────────────┐ Processes view.DocumentFragment │ view.Document │ to text/html and text/plain │ clipboardOutput │ and stores results in data.dataTransfer. └──────────────────────────┘三个环节的默认动作如下源码实现见 clipboardpipeline.tsview.Document#copy/view.Document#cut阶段调用model#getSelectedContent()获取选中内容的model.DocumentFragment调用data.preventDefault()阻止原生复制/剪切默认行为触发ClipboardPipeline#outputTransformation事件内容以模型文档片段形式放在data.content。注意输出阶段还通过ClipboardMarkersUtils把评论/修订等模型标记一并纳入复制片段clipboardpipeline.tsClipboardPipeline#outputTransformation阶段处理data.content模型文档片段将其转换为view.DocumentFragment并触发view.Document#clipboardOutputview.Document#clipboardOutput阶段把data.content视图文档片段以 HTML 形式写入剪贴板若为剪切操作同时调用model.deleteContent()删除编辑器中的选中内容。该阶段还通过viewToPlainTextsrc/utils/viewtoplaintext.ts生成纯文本版本并把当前编辑器 ID 写入application/ckeditor5-editor-id数据项用于同编辑器间复制粘贴时保留标记。此动作由低优先级监听器执行因此普通监听器可覆盖在此阶段其他功能插件也可以加工被复制或剪切的内容。纯文本粘贴PastePlainText使用Ctrl/CmdShiftV粘贴时粘贴进来的文本会匹配目标位置内容的格式即无格式粘贴——源文本的格式被替换为目标位置的格式。功能说明见 features/paste-plain-text.md实现见 src/pasteplaintext.ts。实现要点从源码结构可以确认PastePlainText插件依赖ClipboardPipeline并通过editor.plugins.get( ClipboardPipeline ).on( contentInsertion, ... )挂接输入管线的最后阶段处理器先用isUnformattedInlineContent()判断粘贴内容是否为无格式内联内容纯文本/行内对象、或恰好一个块级元素如pFoobar/p、h2Title/h2且其内容均为内联元素且不含格式化属性时才成立命中后在模型 change 块中保留目标位置的格式化属性通过model.schema.getAttributeProperties( key ).isFormatting筛选选择位置的格式化属性若选择未折叠则先删除被覆盖的旧内容再把保留下来的属性如链接linkHref这类非格式化但应保留的属性逐个写入粘贴内容的每个内联节点上。行为细节粘贴的文本包含双换行时会变成一个段落paragraph包含单换行时会变成软换行soft break。由于PastePlainText随Clipboard插件自动激活因此只需安装Clipboard即可使用无需额外配置。拖拽DragDrop与内容块移动拖拽功能允许在编辑器内拖拽文本以及段落、表格、列表等内容块选中整块或多块内容后在块之前/之后移动也支持从编辑器外部拖入 HTML 与纯文本内容以及配合图片上传使用。功能说明见 features/drag-drop.md。内容块拖拽DragDrop插件src/dragdrop.ts默认完整支持内容块拖拽具体能力包括选择文本、元素、多个块并拖动移动将块放入其他块内部如表格、块引用 blockquote 内在 balloon block 编辑器中drag-indicator拖拽手柄图标现在可作为拖拽把手使用。从源码结构看拖拽流程复用输入管线mousedown时设置draggable属性dragstart检索选中模型片段并转换为视图片段dragover/dragging期间更新放置目标标记由 DragDropTarget 负责dragenter聚焦编辑器视图最终drop落入剪贴板默认处理器并解析最终的data.targetRanges、在拖拽到自身内容上时中止操作事件总览见 src/dragdrop.ts 顶部注释。文件上传拖拽当集成中启用了 CKBox 文件管理器ckbox功能时可以通过拖拽机制上传文件和图片。另外得益于 image-upload 功能也支持从文件系统拖入图片并自动上传。拖拽放置线的样式定制拖拽放置目标线的颜色由 CSS 变量--ck-clipboard-drop-target-color管理可通过如下代码修改颜色主题样式定义见 theme/clipboard.css:root { --ck-clipboard-drop-target-color: green; }无障碍快捷键src/clipboard.ts 在插件初始化时向编辑器的无障碍键盘操作数据库注册了三组快捷键信息CTRLC—— 复制选中内容CTRLV—— 粘贴内容CTRLSHIFTV—— 以纯文本形式粘贴内容。周边生态与验证途径配套演示代码拖拽演示的源码与配置位于 docs/_snippets/features/drag-drop.js、drag-drop.html 与 hcard.js后者实现把 h-card 联系人卡片拖入编辑器的自定义插件纯文本粘贴演示见 docs/_snippets/features/paste-plain-text.js。手动测试manual目录提供 copycut.ts、pasting.ts、dragdrop.ts、dragdrop-blocks.ts 等手动测试页可在浏览器中直观验证复制/粘贴/拖拽行为。单元测试tests目录覆盖 clipboardpipeline.js、clipboardobserver.js、pasteplaintext.js、dragdrop.js 以及 utils 子目录下的 normalizeclipboarddata.js、plaintexttohtml.js、viewtoplaintext.js 等运行测试使用pnpm test定义于 package.json。许可证该包采用双重许可模式见 LICENSE.md 与 包 READMEGNU General Public License Version 2 或更高版本GPL-2.0-or-later或 CKSource Holding sp. z o.o. 提供的商业许可条款。你可以先注册 CKEditor 官方的免费 14 天试用账户来体验完整能力商业功能如协作编辑等生产环境按自身合规需求选择 GPL 或商业授权。小结ckeditor/ckeditor5-clipboard通过拦截原生剪贴板事件 双管线事件系统的方式为 CKEditor 5 提供了可控、可扩展的复制/剪切/粘贴/拖拽能力输入管线paste/drop→clipboardInput→inputTransformation→contentInsertion负责把外部内容安全地转换并插入编辑器输出管线copy/cut→outputTransformation→clipboardOutput负责把选中内容序列化回剪贴板。在此基础上PastePlainText与DragDrop两个随附插件提供了开箱即用的纯文本粘贴与块级拖拽体验而inputTransformation、contentInsertion等公开事件则为自定义功能自动链接化、文件上传、数据格式扩展提供了标准的扩展入口。【免费下载链接】ckeditor5Powerful rich text editor framework with a modular architecture, modern integrations, and features like collaborative editing.项目地址: https://gitcode.com/GitHub_Trending/ck/ckeditor5创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表