剪贴板
剪贴板功能(由 Clipboard
插件实现)负责与本地剪贴板的集成。本地剪贴板是操作系统和浏览器的功能。它在用户在编辑器内或从/到“外部”复制、剪切、粘贴或拖放内容时使用。
CKEditor 5 拦截所有本地事件,如 copy
、cut
或 drop
,并在其自身侧处理它们。目标是不允许浏览器触碰富文本编辑器中的内容。这会导致浏览器将其弄乱。
有两种内容处理方向
这两个管道都允许功能处理要插入或设置到剪贴板的内容。它们还允许在这些过程的不同阶段覆盖默认机制。
# 输入管道
当用户将内容粘贴或拖放到编辑器中时,浏览器会触发事件。剪贴板功能会拦截此事件并启动以下机制
ClipboardObserver
将此事件转换为合成view.Document#paste
或view.Document#drop
。- 两种操作(粘贴和拖放)要插入的内容通常应该以相同的方式处理,并且这两种操作都有类似的效果。因此,这两个事件都被转换为单个
view.Document#clipboardInput
事件,以便于处理。 - 接下来,剪贴板功能监听
view.Document#clipboardInput
事件,并检索和预处理它在 事件的dataTransfer
中找到的text/html
或text/plain
内容。然后,它会使用事件数据content
属性中的检索到的内容作为view.DocumentFragment
触发ClipboardPipeline#inputTransformation
事件。 - 然后,剪贴板功能监听
ClipboardPipeline#inputTransformation
事件。它会获取处理后的内容并将其转换为model.DocumentFragment
。然后,它会使用事件数据content
属性中的转换后的内容作为model.DocumentFragment
触发ClipboardPipeline#contentInsertion
事件。 - 最后,剪贴板功能监听
ClipboardPipeline#contentInsertion
事件。它获取模型片段并 插入 到编辑器中。然后,它将包含所有执行的更改的范围存储在事件数据的resultRange
属性中。
剪贴板功能使用 低优先级监听器 监听 view.Document#clipboardInput
、ClipboardPipeline#inputTransformation
和 ClipboardPipeline#contentInsertion
事件。这意味着添加一个普通监听器并调用 evt.stop()
允许覆盖剪贴板功能实现的行为。这与 DOM 的 evt.preventDefault()
机制类似,它允许你覆盖默认的浏览器行为。
# 输入管道事件概述
┌──────────────────────┐ ┌──────────────────────┐
│ 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 │
└─────────────────────┘
# 以不同方式处理剪贴板输入
默认情况下,剪贴板功能从剪贴板中检索 text/html
或 text/plain
。它会对数据进行一些规范化(例如,清理 空格方面的混乱)。它会将其转换为 视图 DocumentFragment
并在进一步处理时触发 ClipboardPipeline#inputTransformation
事件,其中包含该文档片段。
你可以使用 view.Document#clipboardInput
事件来覆盖此行为。例如,你可以使用它来
-
处理粘贴或拖放的文件。你可以从
dataTransfer
中检索这些文件。但是,处理文件上传需要比读取
dataTransfer.files
多得多。有关完整代码示例,请查看像ImageUploadEditing
这样的插件的源代码。 -
更改剪贴板功能从剪贴板中读取的数据类型。例如,如果
dataTransfer
中存在application/rtf
,你可能希望使用它(在这种情况下忽略text/html
)。editor.editing.view.document.on( 'clipboardInput', ( evt, data ) => { const dataTransfer = data.dataTransfer; const rtfContent = dataTransfer.getData( 'application/rtf' ); // If no RTF was pasted, abort and let the clipboard feature handle the input. if ( !rtfContent ) { return; } // Convert an RTF raw string to a view document fragment. const viewContent = convertRtfStringToView( rtfContent ); // Pass the view fragment to the default clipboard input handler // to allow further processing of the content. data.content = viewContent; } );
# 处理输入内容
ClipboardPipeline#inputTransformation
事件允许你处理将要插入到编辑器中的内容。
默认操作是触发 ClipboardPipeline#contentInsertion
事件。如果数据不为空,此事件将 插入 内容(data.content
,用 DocumentFragment
表示)到编辑器中。
在此阶段,功能可以处理粘贴的内容。例如,你可以实现一个功能,它希望以以下方式将粘贴的文本转换为链接
const writer = new UpcastWriter( 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 ) ]
)
] );
}
} );
默认操作(将内容插入到编辑器中)由低优先级监听器执行,因此它可以被普通监听器覆盖。使用 lowest
优先级,你也可以在内容已插入后执行操作。
editor.plugins.get( 'ClipboardPipeline' ).on( 'contentInsertion', ( evt, data ) => {
console.log( 'Content was inserted.' );
}, { priority: 'lowest' } );
查看 事件系统深入指南,了解有关事件监听器优先级的更多信息。
# 粘贴为纯文本插件示例
你可以使用前面部分的知识来创建一个完整的插件。一个完美的示例是我们的 [PastePlainText](https://github.com/ckeditor/ckeditor5/blob/master/packages/ckeditor5-clipboard/src/pasteplaintext.ts)
,它在按下 Shift 时粘贴纯文本。如果你不熟悉在 CKEditor 5 中创建插件,请先阅读 创建简单插件 指南。
# 输出管道
输出管道等同于输入管道,但用于复制和剪切操作。它允许处理将要放入剪贴板的内容,或覆盖整个过程。
# 输出管道事件概述
┌──────────────────────┐ ┌──────────────────────┐ 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.
└──────────────────────────┘
# 1. 在 view.Document#copy
和 view.Document#cut
上
默认操作是
- 获取选定内容 从编辑器中。
- 阻止本地
copy
或cut
事件的默认操作。 - 使用由 模型文档片段 表示的选定内容触发
clipboard.ClipboardPipeline#outputTransformation
`。
# 2. 在 clipboard.ClipboardPipeline#outputTransformation
上
默认操作是
- 处理由 模型文档片段 表示的
data.content
。 - 使用已处理的
data.content
(已转换为 视图文档片段)触发view.Document#clipboardOutput
。
# 3. 在 view.Document#clipboardOutput
上
默认操作是将内容 (data.content
,表示为一个 DocumentFragment
) 作为 HTML 放入剪贴板。在剪切操作的情况下,选定的内容也会从编辑器中删除。
此操作由一个低优先级监听器执行,因此它可以被一个正常的监听器覆盖。
在此阶段,其他功能可以处理复制或剪切的内容。
我们每天都在努力保持文档的完整性。您是否发现了过时的信息?是否缺少某些内容?请通过我们的 问题跟踪器 报告。
随着 42.0.0 版本的发布,我们重新编写了许多文档,以反映新的导入路径和功能。我们感谢您的反馈,以帮助我们确保文档的准确性和完整性。