【问题标题】:Set cursor to specific position in CKEditor将光标设置到CKEditor中的特定位置
【发布时间】:2013-05-25 23:32:49
【问题描述】:

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

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

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

(我问了related question,但我认为我用示例代码使问题过于复杂。)

【问题讨论】:

    标签: javascript ckeditor contenteditable


    【解决方案1】:

    设置选择的基本方式是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 后恢复选择

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

    Range 保留对其开始和结束容器的引用(在startContainerendContainer 属性中)。不幸的是,以下引用可能会违反:

    • 覆盖innerHTML,
    • 移动 DOM 节点,
    • 正在删除 DOM 节点。

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

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

    首先,这是我们的计划:

    1. 我们得到当前的选择位置。
    2. 我们(以某种方式)存储它。
    3. 我们进行 DOM 更改。
    4. 我们恢复选择。

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

    解决方案 1 - 按范围

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

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

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

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

    createBookmarks 方法创建的书签会在所选范围的起点和终点插入具有特殊属性(包括 data-cke-bookmark)的不可见 &lt;span&gt; 元素。

    如果您可以避免不受控制的innerHTML 更改,而是追加/删除/移动一些节点,那么请记住您必须保留这些&lt;span&gt; 元素,并且此方法将完美运行。如果您的修改也应该改变选择,您也可以移动书签的元素。

    默认情况下,书签保留对其&lt;span&gt; 元素的引用,但您也可以创建可序列化的书签,将true 传递给createBookmarks 方法。这种书签会保留对节点的引用,所以你可以覆盖整个innerHTML

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

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

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

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

    注意:在这个解决方案中,我们使用createBookmarks2 方法。

    这里我们还创建了一个书签对象数组,但是我们没有将任何元素插入到 DOM 中。这些书签通过地址存储它们的位置。 Address 是祖先在其父母中的索引数组。

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

    总结一下……

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

    【讨论】:

    • 优秀的答案 - 真的帮助我更好地理解这个问题。谢谢楼主!
    【解决方案2】:

    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 可能是一个已弃用的函数,我不确定。

    【讨论】:

    • 这很有趣。我想知道@Reinmar 是否有任何意见?在更改编辑器内容后,selectBookmarks 没有任何成功 - 也许moveToBookmark 在我的情况下会更好。
    • 现在似乎已记录在案。 docs.ckeditor.com/#!/api/…
    猜你喜欢
    • 1970-01-01
    • 2013-07-25
    • 1970-01-01
    • 1970-01-01
    • 2011-05-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多