如何在contenteditable元素根级别列表后创建新段落
实现方案
可以实现该需求,且全程基于原生document.execCommand操作,自动兼容浏览器原生undo/redo能力,无需自行维护撤销重做栈。
核心逻辑
监听可编辑元素的回车事件,判断当前是否处于空列表项<li>内(双击回车的第二次触发场景),触发时执行两次outdent命令将当前空节点逐层提升到contenteditable根级别即可:
- 第一次
outdent:将空<li>从列表容器<ol>/<ul>中移出,变为列表的同级元素 - 第二次
outdent:将元素继续向上提升,最终变为contenteditable元素的直接子节点
示例代码
const editableEle = document.querySelector('[contenteditable="true"]'); editableEle.addEventListener('keydown', (e) => { // 仅处理回车键事件 if (e.key !== 'Enter') return; const selection = window.getSelection(); if (!selection.rangeCount) return; const range = selection.getRangeAt(0); let currentNode = range.commonAncestorContainer; // 兼容文本节点场景 if (currentNode.nodeType === Node.TEXT_NODE) { currentNode = currentNode.parentElement; } // 查找当前所在的li元素 const currentLi = currentNode.closest('li'); // 仅当在空li内按回车时触发自定义逻辑 if (!currentLi || currentLi.textContent.trim() !== '') return; // 阻止浏览器默认插入行为 e.preventDefault(); // 两次outdent将节点提升到contenteditable根级 document.execCommand('outdent'); document.execCommand('outdent'); // 插入空白段落 document.execCommand('insertParagraph', false, ''); // 将焦点定位到新段落的起始位置 const newParagraph = editableEle.lastElementChild; const newRange = document.createRange(); newRange.selectNodeContents(newParagraph); newRange.collapse(true); selection.removeAllRanges(); selection.addRange(newRange); });
注意事项
如果你的contenteditable内的嵌套层级和示例不同,可根据实际嵌套深度调整outdent的执行次数,所有execCommand操作都会被原生记录到撤销栈中,ctrl+z/ctrl+y可正常回滚/重做。
内容的提问来源于stack exchange,提问作者David Dal Busco
相关产品推荐
相关产品推荐

