如何从CKEditor 5中的Insert事件中获取文本?

Mic*_*den 5 javascript ckeditor ckeditor5

我正在尝试从CKEditor 5处理插入事件.

editor.document.on("change", (eventInfo, type, data) => {
  switch (type) {
    case "insert":
    console.log(type, data);
    break;
  }
});
Run Code Online (Sandbox Code Playgroud)

在编辑器中键入时,将调用回调.data事件回调中的参数看起来像这样:

{
  range: {
    start: {
      root: { ... },
      path: [0, 14]
    },
    end: {
      root: { ... },
      path: [0, 15]
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

我没有看到一种方便的方法来确定实际插入的文本.我可以调用data.range.root.getNodeByPath(data.range.start.path);哪个似乎得到了插入文本的文本节点.那么我们应该查看文本节点的data字段吗?我们是否应该假设路径中的最后一项始终是范围开始和结束的偏移量并使用它来进行子串?我认为插入事件也会因为插入非文本类型的东西而被触发(例如元素).我们怎么知道这确实是事件的文本类型?

有没有我想念的东西,或者只有不同的方式一起完成这一切?

Szy*_*lik 2

首先,让我描述一下您目前(2018 年 1 月)将如何做。请记住,CKEditor 5 现在正在进行大规模重构,情况将会发生变化。最后,我将描述完成这次重构后它会是什么样子。如果你不介意再等一段时间重构结束,你可以跳到后面的部分。

编辑:1.0.0-beta.1版本于 3 月 15 日发布,因此您可以跳转到“自 2018 年 3 月以来”部分。

至 2018 年 3 月(至1.0.0-alpha.2

(如果您需要了解有关某些类 API 或事件的更多信息,请查看文档。)

您最好的选择就是简单地迭代插入的范围。

let data = '';

for ( const child of data.range.getItems() ) {
    if ( child.is( 'textProxy' ) ) {
        data += child.data;
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,一个TextProxy您迭代范围时,始终会返回一个实例,即使整个Text节点都包含在范围中。

(您可以阅读有关对范围进行字符串化的更多信息- Getting精确位置的插入符号单击编辑器内部以获取数据。)

请记住,这InsertOperation可能会插入多个不同类型的节点。大多数情况下,这些只是单个字符或元素,但可以提供更多节点。这就是为什么没有额外的data.item或类似的财产data。可能有data.items,但那些与 相同Array.from( data.range.getItems() )

进行更改Document#change

您没有提到您之后想用这些信息做什么。获取范围的内容很容易,但如果您想以某种方式对这些更改做出反应并更改模型,那么您需要小心。当。。。的时候change事件被触发时,可能已经有更多的更改排队。例如:

  • 协作服务可以立即带来更多变化,
  • 不同的功能可能已经对相同的更改做出了反应,并将其更改排入队列,这可能会使模型有所不同。

如果您确切知道将使用哪一组功能,则可以坚持我建议的内容。请记住,您对模型所做的任何更改都应该在Document#enqueueChanges()(否则,它将不会被渲染)。

如果您想让这个解决方案防弹,您可能必须这样做:

  1. 在迭代子项时data.range,如果您找到TextProxy,则创建一个LiveRange跨越。
  2. 然后,在一个enqueueChanges()块中,迭代存储的LiveRanges 及其子代。
  3. 对每个发现的内容进行逻辑分析TextProxy
  4. 记住之后destroy()所有的LiveRanges。

正如您所看到的,这似乎不必要地复杂。提供开放且灵活的框架(例如 CKE5)有一些缺点,考虑到所有边缘情况就是其中之一。然而,它确实可以更简单,这就是我们首先开始重构的原因。

自2018年3月起(自1.0.0-beta.1

1.0.0-beta.1 中的重大变化将是引入model.Differ、改进的事件结构以及模型大部分的新 API。

首先,Document#event:change终究会被解雇enqueueChange将在所有块完成这意味着您不必担心另一个更改是否会干扰您在回调中做出反应的更改。

此外,engine.Document#registerPostFixer()还将添加方法,您将能够使用它来注册回调。change活动仍然可用,但change活动和活动之间会有细微差别registerPostFixer之间会有细微差别(我们将在指南和文档中介绍它们)。

其次,您将可以访问model.Differ实例,该实例将存储第一次更改之前的模型状态与您想要对更改做出反应时的模型状态之间的差异。您将迭代所有差异项目并检查到底发生了什么以及哪里发生了变化。

除此之外,重构中还将进行许多其他更改,下面的代码片段也将反映它们。所以,在新世界中,它会是这样的:

editor.document.registerPostFixer( writer => {
    const changes = editor.document.differ.getChanges();

    for ( const entry of changes ) {
        if ( entry.type == 'insert' && entry.name == '$text' ) {
            // Use `writer` to do your logic here.
            // `entry` also contains `length` and `position` properties.
        }
    }
} );
Run Code Online (Sandbox Code Playgroud)

就代码而言,它可能比第一个片段中的代码多一点,但是:

  1. 第一个片段不完整。
  2. 新方法中需要考虑的边缘情况要少得多。
  3. 新方法更容易掌握 - 在完成所有更改后,您可以使用所有更改,而不是在其他更改排队时对更改做出反应,并且可能会扰乱模型。

writer一个将用于对模型进行更改的对象(而不是Document#batchAPI)。它将有类似insertText(), insertElement(),remove()等方法。

您可以检查API 和测试,因为它们已经在分支model.Differ上可用。(内部代码会改变,但API将保持原样。)master