将光标移至CKEditor中的特定位置

有没有办法将光标位置设置为CKEditor中已知的索引?

我想这样做,因为当我更改编辑器中的HTML将光标重置到插入的元素的开始,这是一个问题,因为我正在用户input时更改内容。

如果我知道我想把光标放回到编辑器内已知的字符位置,比如说100,这是可能的吗?

(我问了一个相关的问题,但我认为我用示例代码来解决这个问题。)

设置select的基本方法是创build一个范围 ,设置其位置并select它。

注意 :如果你不知道范围API(或至less是范围后面的想法),你将无法使用select。 这里有一个相当不错的介绍 – DOM范围规范 (是的,这是一个规范,但它是好的)。 CKEditor的Range API非常相似,但稍大一些。

例如:

// Having this HTML in editor: // <p id="someId1">foo <em id="someId2">bar</em>.</p> var range = editor.createRange(); range.setStart( editor.document.getById( 'someId1' ), 0 ); // <p>^foo range.setEnd( editor.document.getById( 'someId2' ).getFirst(), 1 ); // <em>b^ar</em> editor.getSelection().selectRanges( [ range ] ); // Will select: // <p id="someId1">[foo <em id="someId2">b]ar</em>.</p> 

或者其他情况:

 // Having this HTML in editor: // <p>foo bar.</p> var range = editor.createRange(); range.moveToElementEditablePosition( editor.editable(), true ); // bar.^</p> editor.getSelection().selectRanges( [ range ] ); // Will select: // <p>foo bar.^</p> 

更改DOM后恢复select

但很多时候你不想select一个新的范围,而是要恢复一个旧的select或范围。 首先,您需要知道的是, 如果您做出不受控制的DOM更改 ,则无法正确恢复select 。 您需要能够跟踪select开始和结束的容器和偏移量。

范围保持对其开始和结束容器的引用(在startContainerendContainer属性中)。 不幸的是,这个引用可能被违反:

  • 改写innerHTML
  • 移动DOM节点,
  • 删除DOM节点。

偏移量( startOffsetendOffset属性)可能会发生同样的情况 – 如果您删除了开始/结束容器的子节点之一,则可能需要更新这些偏移量。

所以在某些情况下,当我们想要记住一个select的位置时,范围实例是没有用的。 我将解释处理这个问题的三种基本方法。

首先,这是我们的计划:

  1. 我们得到当前的select位置。
  2. 我们存储它(不知何故)。
  3. 我们做DOM的变化。
  4. 我们恢复select。

注意:从现在开始,我使用复数forms的“范围”,因为Firefox支持多个范围select – 一个select可以包含多个范围(例如,尝试在select时使用CTRL键)。

解决scheme1 ​​ – 一个范围

 var ranges = editor.getSelection().getRanges(); // Make DOM changes. editor.getSelection().selectRanges( ranges ); 

这是最简单的解决scheme。 只有在我们所做的DOM更改没有过期的范围内,或者我们知道如何更新它们时,它才会起作用。

解决scheme2 – 通过侵入性书签

 var bookmarks = editor.getSelection().createBookmarks(); // Make DOM changes. editor.getSelection().selectBookmarks( bookmarks ); 

createBookmarks方法创builddata-cke-bookmark在select范围的起点和终点处插入不可见的具有特殊属性(包括data-cke-bookmark )的<span>元素。

如果您可以避免不受控制的innerHTML更改,而是附加/移除/移动某些节点,那么请记住,您必须保留这些<span>元素,并且此方法将完美工作。 如果您的修改也应该改变select,您也可以移动书签的元素。

默认情况下,书签保留对其<span>元素的引用,但是也可以创build可序列化的书签,传递给createBookmarks方法。 这种书签将通过id保持对节点的引用,所以你可以覆盖整个innerHTML

注意:这个方法在Range API中也是可用的。

这是最stream行的方法,因为您可以完全控制select,并且可以更改DOM,但您需要关注书签的spans

解决scheme3 – 由一个非侵入性的书签

 var bookmarks = editor.getSelection().createBookmarks2(); // Make DOM changes. editor.getSelection().selectBookmarks( bookmarks ); 

注意:在这个解决scheme中,我们使用createBookmarks 2方法。

这里我们也创build了一个书签对象数组,但是我们不插入任何元素到DOM中。 这些书签通过地址存储他们的位置。 地址是父母祖先的一系列索引。

此解决scheme与解决scheme1非常相似,但是您可以覆盖整个innerHTML ,因为它(很可能;>)不会更改书签节点的地址。 虽然,在这种情况下,您应该传递createBookmarks2来获得规范化的地址,因为在设置innerHTML时,相邻的文本节点将被连接,而空的连接被删除。

总结一下…

…使用DOM和select不是微不足道的。 你需要知道你在做什么,你需要知道DOM,你需要为你的问题select正确的解决scheme。 大多数情况下,这将是第二个,但这取决于一个案例。

Reinmar的回答让我想到了这个解决scheme

 var selection = ed.getSelection(); var bookmarks = selection.createBookmarks(true); //delete text from editor var range = selection.getRanges()[0]; range.moveToBookmark(bookmarks[0]); range.select(); 

注意:moveToBookmark函数没有logging在API中,但是非常有用,是唯一的解决scheme,为我工作。 我当然不是ckeditor的专家,并花了我几天的时间find一个可行的解决scheme。 所以moveToBookmark可能是我不确定的弃用函数。