如何仅使用Vanilla JS开发不依赖execCommand及第三方库的富文本编辑器?
纯Vanilla JS实现无execCommand的富文本编辑器
当然可以实现,核心是靠contenteditable属性搭配原生的Selection和Range API直接操作DOM,完全不需要execCommand或者第三方库。下面是具体的实现思路和代码示例:
核心原理
给编辑器容器设置contenteditable="true"让它可编辑,然后通过window.getSelection()获取用户选中的内容范围,用Range对象来修改、包裹或插入DOM元素,以此实现各种富文本格式。
常见功能实现示例
1. 加粗文本
function boldText() { const selection = window.getSelection(); if (!selection.rangeCount) return; const range = selection.getRangeAt(0); const strong = document.createElement('strong'); if (range.collapsed) { // 没有选中内容时,插入空的加粗标签并定位光标 range.insertNode(strong); range.selectNodeContents(strong); selection.removeAllRanges(); selection.addRange(range); } else { // 有选中内容时,用strong标签包裹选中区域 range.surroundContents(strong); // 重新选中包裹后的内容 const newRange = document.createRange(); newRange.selectNodeContents(strong); selection.removeAllRanges(); selection.addRange(newRange); } }
2. 插入链接
function insertLink(url) { const selection = window.getSelection(); if (!selection.rangeCount) return; const range = selection.getRangeAt(0); const link = document.createElement('a'); link.href = url; link.textContent = selection.toString() || url; if (range.collapsed) { range.insertNode(link); // 光标移到链接后面 range.setStartAfter(link); selection.removeAllRanges(); selection.addRange(range); } else { range.surroundContents(link); } }
3. 切换无序列表
function toggleUnorderedList() { const selection = window.getSelection(); if (!selection.rangeCount) return; const range = selection.getRangeAt(0); const listItem = range.commonAncestorContainer.closest('li'); if (listItem) { // 已经在列表中,取消列表格式 const list = listItem.parentElement; const content = listItem.firstChild; list.parentNode.insertBefore(content, list); listItem.remove(); if (!list.children.length) list.remove(); } else { // 不在列表中,创建新的无序列表 const ul = document.createElement('ul'); const li = document.createElement('li'); if (range.collapsed) { li.appendChild(document.createTextNode('')); ul.appendChild(li); range.insertNode(ul); range.selectNodeContents(li.firstChild); } else { range.surroundContents(li); ul.appendChild(li); range.insertNode(ul); } selection.removeAllRanges(); selection.addRange(range); } }
关键注意事项
- 选区维护:每次修改DOM后要重新设置选区,避免光标丢失或选中状态异常。
- 内容保存:直接获取编辑器元素的
innerHTML即可保存富文本内容,也可以自行转换为Markdown格式。 - 撤销/重做:需要自己维护操作历史栈,记录每次修改前的DOM状态,实现撤销功能。
- 兼容性:Selection和Range API在现代浏览器中支持良好,无需担心兼容问题。
内容的提问来源于stack exchange,提问作者Sakthi
相关产品推荐
相关产品推荐

