Fork me on Bolg '◡'

cke点击时初始化编辑器后光标恢复的方法

业务场景

1、使用了CKEDITOR编辑器
2、文本是使用contenteditable="true"的div容器
3、点击文本时才初始化编辑器
4、问题:编辑器初始化后光标会重置到开始处,如何将光标重置到点击处

解决方案

1、在点击文本的时候,在点击文本的时候,获取range信息和 endContainerendOffset;

      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多的时间。如果你有更好的想法,希望能跟我留言~

posted @ 2021-04-09 18:14  webhmy  阅读(245)  评论(0编辑  收藏  举报