SOURCE

console 命令行工具 X clear

                    
>
console
const editorElem = document.getElementById('editor');
const contentElem = document.getElementById('content');
const btnElem = document.getElementById('btn');
let selection = null;
let range = null;

const setSelection = () => {
  selection = window.getSelection();
  range = selection.getRangeAt(0);
};

// 有时候在 a 标签后面回车,会将 br 插入到 a 标签里面,而不是在 a 标签后面
// 这里的主要逻辑是,如果当前光标所在的上下文是 a 标签,则重新创建 range,将 range 调整到外层的 div 上,range 的起始和终点位置设置到当前元素的后面一个位置上
const createRange = () => {
  if(!range) {
    return;
  }
  const endContainer = range.endContainer;
  const currentElem = endContainer.parentElement;
  if(currentElem.localName === 'a') {
    range = document.createRange();
    const startNode = currentElem.parentElement;
    const offset = getElemIdx(currentElem) + 1;
    range.setStart(startNode, offset);
    range.collapse(true);
  }
};

const getElemIdx = (elem) => {
  const childNodes = Array.from(editorElem.childNodes);
  const idx = childNodes.findIndex((node) => node === elem);
  return idx !== -1 ? idx : 0;
};

const insertNodeAtCursor = (node) => {
  if(range.startOffset !== range.endOffset) {
    range.deleteContents()
  }
  range.insertNode(node);
  range.setStartAfter(node);
  range.collapse(true);
  selection.removeAllRanges();
  selection.addRange(range);
};

editorElem.addEventListener('keydown', (e) => {
  const keyCode = e.keyCode || e.which;
  if(keyCode === 13) {
    // 如果是在最后一行的末尾回车,需要添加两个 br 才能换行。否则需要按两次回车才能换行。
    if(editorElem.lastChild && editorElem.lastChild.localName !== 'br') {
      editorElem.appendChild(document.createElement('br'));
    }
    setSelection();
    createRange();
    insertNodeAtCursor(document.createElement('br'));
    e.preventDefault();
  }
  contentElem.innerText = editorElem.innerHTML;
});

editorElem.addEventListener('blur', (e) => {
  setSelection();
});

btnElem.addEventListener('click', (e) => {
  const aElem = document.createElement('a');
  aElem.href = 'https://www.maiscrm.com';
  aElem.text = '群脉 SCRM';
  createRange();
  insertNodeAtCursor(aElem);
  contentElem.innerText = editorElem.innerHTML;
});
<button id="btn" class="btn">点击插入a标签</button>
<div id="editor" class="editor-box" contenteditable="true"></div>
<div id="content"></div>
.editor-box {
  width: 100%;
  height: 100px;
  margin: 20px 0;
  border: 1px solid #ccc;
  overflow: auto;
}