CKEditor限制仅插入一条水平分隔线,报错“给定范围不在文档中”求助
解决CKEditor中替换唯一水平线的报错问题
这个报错"The given range isn't in document"的核心原因很明确:你调用setData(savedContent)后,编辑器的整个文档结构被完全替换了——之前通过getRanges()获取的选区范围是基于旧文档的DOM节点的,新文档里根本找不到这些节点,自然无法选中。而且你的初始思路还有个隐藏问题:用实例化时的savedContent重置内容,会把用户后续编辑的所有内容都清空,这显然不符合需求(用户只是想替换水平线,不是重置整个内容)。
正确的实现思路
我们不需要重置整个内容,只需要:
- 移除编辑器中所有已有的
<hr>元素 - 记录用户点击插入按钮时光标的位置(用CKEditor的书签功能,避免依赖DOM节点)
- 在移除旧水平线后的光标位置插入新的水平线
修正后的代码
CKEDITOR.on('instanceReady', function() { // 获取编辑器实例 const editor = CKEDITOR.instances['pro_story']; // 修改水平线按钮的标题 document.querySelector('.cke_button__horizontalrule').setAttribute('title', 'End Preview here'); // 绑定按钮点击事件 document.querySelector('.cke_button__horizontalrule').addEventListener('click', function(e) { e.preventDefault(); // 阻止默认的插入行为 // 1. 创建当前选区的书签(CKEditor的抽象位置标记,不依赖DOM) const bookmark = editor.getSelection().createBookmark(); // 2. 移除所有已有的水平线 editor.document.find('hr').forEach(hrElement => { hrElement.remove(); }); // 3. 恢复选区到书签标记的位置 editor.getSelection().selectBookmark(bookmark); // 4. 插入新的水平线 editor.insertHtml('<hr />'); }); });
关键细节解释
- 书签(Bookmark)的作用:CKEditor的书签是对内容位置的抽象记录,不会因为DOM结构的局部修改(比如移除
<hr>)而失效,这比直接保存选区范围可靠得多。 - 阻止默认行为:必须用
e.preventDefault()关掉按钮的默认插入逻辑,否则会同时插入默认的水平线和我们自定义的,导致重复。 - 保留用户编辑内容:这个方案不会重置整个内容,只会移除旧的水平线,完全保留用户之前的所有编辑操作,符合需求。
额外优化(可选)
如果想避免用户通过粘贴或其他方式插入多个水平线,可以监听编辑器的change事件,每次内容变化时检查并移除多余的水平线:
editor.on('change', function() { const hrElements = editor.document.find('hr'); if (hrElements.length > 1) { // 保留第一个,移除后面的;或者保留最后一个,根据需求调整 for (let i = 1; i < hrElements.length; i++) { hrElements[i].remove(); } } });
内容的提问来源于stack exchange,提问作者S R
相关产品推荐
相关产品推荐

