业务场景
1、使用了CKEDITOR编辑器
2、文本是使用contenteditable="true"的div容器
3、点击文本时才初始化编辑器
4、问题:编辑器初始化后光标会重置到开始处,如何将光标重置到点击处
解决方案
1、在点击文本的时候,在点击文本的时候,获取range
信息和 endContainer
与endOffset
;
try {
range = window.getSelection().getRangeAt(0);
var endContainer = range.endContainer;
var endOffset = range.endOffset;
} catch (e) {
console.log(e);
}
2、坑:直接点击图片,无法获取到range,需要把图片加入到range中;
// 把图片加进选区
if (e.target.nodeName === 'IMG') {
var range2 = document.createRange();
var selectTion = window.getSelection();
selectTion.removeAllRanges();
range2.selectNode(e.target);
selectTion.addRange(range2);
}
3、在初始化编辑器后,在它的instanceReady
方法回调中进行光标恢复操作
CKEDITOR.instances[id].once('instanceReady', () => {
...
});
听起来很美好,对不对,但是在实际操作中发现,之前存储的
endContainer
已经被替换成新的range信息了。如何获取我们原来存储的endContainer
呢?博主试了深拷贝,浅拷贝都不行,于是才用了遍历寻找的方法~~~
4、找到原来的endContainer
的方法
// 获取新的endContainer
getEndContainer(endContainer, tag, endContainer2) {
let childNodes = self.getAllChildNodes(tag);
if (childNodes && childNodes.length > 0) {
for (let i = 0, len = childNodes.length; i < len; i++) {
let item = childNodes[i];
if (
(item.data && item.data === endContainer.data) ||
(item.wholeText && item.wholeText === endContainer.wholeText) ||
(item.innerHTML && item.innerHTML === endContainer.innerHTML)
) {
// 为了避免有重复片段,必须其父亲也要相同,我这里的每行父级元素都有类名cut-check
let $endContainerParent = $(endContainer).hasClass('cut-check') ? $(endContainer) : $(endContainer).parents('.cut-check');
let $itemParent = $(item).hasClass('cut-check') ? $(item) : $(item).parents('.cut-check');
if ($endContainerParent.attr('data-value') === $itemParent.attr('data-value')) {
endContainer2 = item;
break;
}
}
}
}
return endContainer2;
}
5、恢复光标:这里需要再加个延迟,不然光标还没有初始化到开头处
CKEDITOR.instances[id].once('instanceReady', () => {
setTimeout(() => {
try {
var newRange = document.createRange();
var set = window.getSelection();
// 找到与 endContainer 一样的节点
let endContainer2 = null;
endContainer2 = self.getEndContainer(endContainer, tag, endContainer2);
newRange.setEnd(endContainer2, endOffset);
newRange.collapse(false);
set.removeAllRanges();
set.addRange(newRange);
} catch (e) {
console.log(e)
}
}, 500)
});
6、坑:到这里,我们已经实现了光标恢复,但是会发现光标会现在起始处闪一下,再闪到我们点击处。作为有强迫症的前端肯定不能容忍这种操作了,这个我们使用样式就可以处理了:在开始点击的时候给目标div添加一个隐藏光标的样式,然后恢复光标的时候移除即可。
.hide-caret {
caret-color: transparent;
}
让我们再看下效果:
结束语
至此,完成了我们的业务需求,当然第一次点击的时候会有不可见的1-2s的延迟,但是没办法,编辑器的初始化就需要1s多的时间。如果你有更好的想法,希望能跟我留言~