将光标移至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开始和结束的容器和偏移量。
范围保持对其开始和结束容器的引用(在startContainer
和endContainer
属性中)。 不幸的是,这个引用可能被违反:
- 改写
innerHTML
, - 移动DOM节点,
- 删除DOM节点。
偏移量( startOffset
和endOffset
属性)可能会发生同样的情况 – 如果您删除了开始/结束容器的子节点之一,则可能需要更新这些偏移量。
所以在某些情况下,当我们想要记住一个select的位置时,范围实例是没有用的。 我将解释处理这个问题的三种基本方法。
首先,这是我们的计划:
- 我们得到当前的select位置。
- 我们存储它(不知何故)。
- 我们做DOM的变化。
- 我们恢复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可能是我不确定的弃用函数。