有没有办法将光标位置设置为CKEditor内的已知索引?
我想这样做,因为当我在编辑器中更改html时,它将光标重置为插入元素的开头,这是一个问题,因为我正在用户输入时动态更改内容。
如果我知道我想将光标设置回编辑器中的已知字符位置(例如100),这可能吗?
(我问related question,但我认为我的示例代码问题过于复杂。)
答案 0 :(得分:50)
设置选择的基本方法是creating和Range,设置其位置并selecting。
注意:如果您不知道Range API(或至少知道落后于范围的想法),您将无法使用选择。这是一个非常好的介绍 - DOM Range spec(是的,这是一个规范,但它很好)。 CKEditor's 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更改,无法正确恢复选择。您需要能够跟踪选择的开始和结束的容器和偏移。
Range保留对其开始和结束容器的引用(在startContainer
和endContainer
属性中)。不幸的是,这些引用可能会被违反:
innerHTML
,偏移量(startOffset
和endOffset
属性)可能会发生同样的情况 - 如果您删除了一个开始/结束容器的子节点,则可能需要更新这些偏移量。
因此,在某些情况下,当我们想要记住选择位置时,范围实例没有帮助。我将解释处理这个问题的三种基本方法。
首先,这是我们的计划:
注意:从现在开始,我使用复数形式的“范围”,因为Firefox支持多个范围选择 - 一个选择可以包含多个范围(例如,在选择时尝试使用CTRL键)。
var ranges = editor.getSelection().getRanges();
// Make DOM changes.
editor.getSelection().selectRanges( ranges );
这是最简单的解决方案。只有当我们制作的DOM更改没有过时范围或者我们知道如何更新它们时,它才会起作用。
var bookmarks = editor.getSelection().createBookmarks();
// Make DOM changes.
editor.getSelection().selectBookmarks( bookmarks );
createBookmarks
方法创建的书签在选择范围的起点和终点处插入具有特殊属性(包括<span>
)的不可见data-cke-bookmark
元素。
如果您可以避免不受控制的innerHTML
更改,而是添加/删除/移动某些节点,那么请记住您必须保留这些<span>
元素,此方法才能完美运行。如果您的修改也应更改选择,您也可以移动书签元素。
默认情况下,书签会保留对其<span>
元素的引用,但您也可以创建将true
传递给createBookmarks
方法的可序列化书签。这种书签将通过ID保持对节点的引用,因此您可以覆盖整个innerHTML
。
注意:此方法也可在Range API中使用。
这是最受欢迎的方法,因为您可以完全控制选择,但您可以更改DOM,但需要处理书签“spans
。
var bookmarks = editor.getSelection().createBookmarks2();
// Make DOM changes.
editor.getSelection().selectBookmarks( bookmarks );
注意:在此解决方案中,我们使用createBookmarks
2
方法。
这里我们还创建了一个书签对象数组,但是我们不会在DOM中插入任何元素。这些书签按地址存储其位置。 Address是父母中一系列祖先的索引。
此解决方案与解决方案1非常相似,但您可以覆盖整个innerHTML
,因为它(很可能是;&gt;)不会更改书签节点的地址。虽然,在这种情况下,您应该将true
传递给createBookmarks2
以获取规范化地址,因为在设置innerHTML
时,相邻的文本节点将被连接并删除空文本节点。
...使用DOM和选择并非易事。你需要知道你在做什么,你需要知道DOM,你需要为你的问题选择正确的解决方案。大多数情况下它会是第二个,但这取决于案例。
答案 1 :(得分:7)
Reinmar的回答让我得到了这个解决方案
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函数没有在api中记录,但非常有用,是唯一对我有用的解决方案。我当然不是ckeditor的专家,并花了几天时间找到一个有效的解决方案。所以moveToBookmark可能是一个我不确定的弃用函数。