将光标设置为CKEditor中的特定位置

man*_*nan 14 javascript contenteditable ckeditor

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

我想这样做是因为当我更改编辑器中的html时,它会将光标重置为插入元素的开头,这是一个问题,因为我在用户输入时动态更改内容.

如果我知道我想将光标设置回编辑器中的已知字符位置(例如100),这可能吗?

(我问了一个相关的问题,但我认为我用示例代码使问题过于复杂.)

Rei*_*mar 51

设置选择的基本方法是创建一个范围,设置其位置并选择它.

注意:如果您不知道Range API(或至少知道落后于范围的想法),您将无法使用选择.这是一个非常好的介绍 - DOM Range规范(是的,它是一个规范,但它很好).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>
Run Code Online (Sandbox Code Playgroud)

或者其他情况:

// 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>
Run Code Online (Sandbox Code Playgroud)

更改DOM后恢复选择

但通常您不希望选择新范围,而是恢复旧选择或范围.您需要知道的第一件事是,如果您进行了不受控制的DOM更改,则无法正确恢复选择.您需要能够跟踪选择的开始和结束的容器和偏移量.

Range保留对其开始和结束容器(in startContainerendContainerproperties)的引用.不幸的是,这些引用可能会被违反:

  • 覆盖innerHTML,
  • 移动DOM节点,
  • 删除DOM节点.

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

因此,在某些情况下,当我们想要记住选择位置时,范围实例没有帮助.我将解释处理这个问题的三种基本方法.

首先,这是我们的计划:

  1. 我们得到当前的选择位置.
  2. 我们存储它(不知何故).
  3. 我们做DOM更改.
  4. 我们恢复选择.

注意:从现在开始,我使用复数形式的"范围",因为Firefox支持多个范围选择 - 一个选择可以包含多个范围(尝试例如在进行选择时使用CTRL键).

解决方案1 ​​ - 按范围划分

var ranges = editor.getSelection().getRanges();

// Make DOM changes.

editor.getSelection().selectRanges( ranges );
Run Code Online (Sandbox Code Playgroud)

这是最简单的解决方案.只有当我们制作的DOM更改没有过时范围或者我们知道如何更新它们时,它才会起作用.

解决方案2 - 通过侵入性书签

var bookmarks = editor.getSelection().createBookmarks();

// Make DOM changes.

editor.getSelection().selectBookmarks( bookmarks );
Run Code Online (Sandbox Code Playgroud)

由该createBookmarks方法创建的书签在选择的范围起点和终点处插入<span>具有特殊属性(包括data-cke-bookmark)的不可见元素.

如果你可以避免不受控制的innerHTML更改,而是追加/删除/移动一些节点,那么只需记住你必须保留这些<span>元素,这种方法将完美地工作.如果您的修改也应更改选择,您也可以移动书签元素.

默认情况下,书签会保留对其<span>元素的引用,但您也可以创建传递true给该createBookmarks方法的可序列化书签.这种书签将通过id保持对节点的引用,因此您可以覆盖整个innerHTML.

注意:此方法也可在Range API中使用.

这是最流行的方法,因为您可以完全控制选择,并且您可以更改DOM,尽管您需要处理书签spans.

解决方案3 - 通过非侵入式书签

var bookmarks = editor.getSelection().createBookmarks2();

// Make DOM changes.

editor.getSelection().selectBookmarks( bookmarks );
Run Code Online (Sandbox Code Playgroud)

注意:在此解决方案中,我们使用createBookmarks2方法.

这里我们还创建了一个书签对象数组,但是我们不会在DOM中插入任何元素.这些书签按地址存储其位置.地址是父母中祖先索引的数组.

此解决方案与解决方案1非常相似,但您可以覆盖整个解决方案innerHTML,因为它(很可能;>)不会更改书签节点的地址.虽然,在这种情况下,您应该传递truecreateBookmarks2标准化地址,因为相邻的文本节点将被连接,并且在设置时将删除空的节点innerHTML.

总结一下...

...使用DOM和选择并非易事.你需要知道你在做什么,你需要知道DOM,你需要为你的问题选择正确的解决方案.大多数情况下它将是第二个,但它取决于案例.


cod*_*ine 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();
Run Code Online (Sandbox Code Playgroud)

注意:moveToBookmark函数没有在api中记录,但是非常有用,并且是唯一对我有用的解决方案.我当然不是ckeditor的专家,并花了几天时间找到一个有效的解决方案.所以moveToBookmark可能是一个我不确定的弃用函数.

  • 现在似乎有记录.http://docs.ckeditor.com/#!/api/CKEDITOR.dom.range-method-moveToBookmark (4认同)