You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何仅使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 09:22:55