execCommand实现富文本编辑器

前言

近期研究了富文本编辑器相关内容,在网上找了很多相关的资料进行学习。发现富文本编辑器相关的资料比较零碎,无法形成系统性知识,这里通过系统性的专题记录下了解到的关于富文本相关知识。

富文本编辑器发展历程

开源的富文本编辑器技术通常长期的发展,可以分为三个阶段:

  1. L0

编辑器技术的起始阶段,依赖于浏览器原生编辑能力。通过浏览器execCommand方法,实现对编辑内容的加粗、斜体、回车...。execCommand是浏览器的原生api,允许通过命令操纵可编辑区域(contenteditable)的元素。还不了解execCommand的同学可以去MND document.execCommand 看看。

  1. L1

基于浏览器contentditable富文本的编辑能力,自定义实现编辑区域操作命令。在浏览器基础能力和排版能力上,满足大部分编辑器自定义需求。L1能力的编辑器代表如下:

  • Quill.js 基于浏览器 contenteditable 编辑能力实现的富文本编辑器,石墨文档就是基于Quill实现的
  • ProseMirror也是依赖于浏览器contenteditable能力,引入了Schema、插件系统、Virtual DOM等等的富文本编辑器,国外的产品Confluence 就是基于ProseMirror,由此可见ProseMirror的稳定性和扩展性强大。近期研究富文本编辑器也是主要看ProseMirror相关内容,后面其他文章会基于ProseMirror原理来分享编辑器相关知识。
  • Draft.js,Facebook团队推出的把富文本编辑器和React结合的富文本作品。在编辑器中引入了状态管理、Immutable等等React的概念。
  • Slate.js相对于其他编辑器框架,最晚推出。站在巨人的肩膀上,借鉴吸收了ProseMirror、Quill、Draft的优点。Slate.js只提供了编辑器的基础能力,定制化极强。很多富文本编辑器团队都会给予Slate做二次开发,定制自己团队的编辑器。钉钉文档就是基于Slate.js开发的。
  1. L2

不依赖浏览器的编辑能力,自己实现选区、光标、排版。目前只有Google Doc实现了L2能力的富文本编辑器,开发难度以及成本很大。

基于execCommand实现简单的L0富文本编辑器

通过执行document.execCommand,操纵可编辑器区域内容的元素。 document.execCommand还支持很多文本编辑能力,感兴趣的同学可以自己查阅文档进行添加。 代码如下:

js 复制代码
// Editor.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }
    #menu-bar {
      margin: 100px 60px 10px;
      display: flex;
    }
    .btn {
      padding: 4px;
      margin-right: 8px;
      border-radius: 2px;
      border: 1px solid #ccc;
      background: #82c8a0;
      color: #fff;
    }
    #editor {
      height: 600px;
      padding: 10px 4px;
      margin: 0px 60px;
      outline: none;
      border: 1px solid #ccc;
    }
  </style>
</head>
<body>
  <div id="menu-bar">
    <button id="bold" class="btn">加粗</button>
    <button id="italic" class="btn">斜体</button>
    <button id="underline" class="btn">下划线</button>
    <button id="justifyCenter" class="btn">居中</button>
    <button id="justifyLeft" class="btn">左对齐</button>
    <button id="justifyRight" class="btn">右对齐</button>
    <button id="insertOrderedList" class="btn">有序列表</button>
    <button id="insertUnorderedList" class="btn">无序列表</button>
    <button id="h1" class="btn">H1</button>
    <button id="h2" class="btn">H2</button>
    <button id="" class="btn">...</button>
  </div>
  <div id="editor" contenteditable="true"></div>
</body>
<script src="./editor.js"></script>
</html>
js 复制代码
// Editor.js
function bold() {
  //加粗
  return document.execCommand('bold', false, null);
}

function italic() {
  // 斜体
  return document.execCommand('italic', false, null);
}

function underline() {
  // 下划线
  return document.execCommand('underline', false, null);
}

function justifyCenter() {
  // 居中
  return document.execCommand('justifyCenter', false, null);
}

function justifyLeft() {
  // 左对齐
  return document.execCommand('justifyLeft', false, null);
}

function justifyRight() {
  // 右对齐
  return document.execCommand('justifyRight', false, null);
}

function h1() {
  // 1级标题
  return document.execCommand('fontSize', false, 7);
}

function h2() {
  // 2级标题
  return document.execCommand('fontSize', false, 6);
}

const menuButtons = {
  'bold': bold,
  'italic': italic,
  'underline': underline,
  'justifyCenter': justifyCenter,
  'justifyLeft': justifyLeft,
  'justifyRight': justifyRight,
  'h1': h1,
  'h2': h2
  //...
};

function setupEditor() {
  const menuBars = document.getElementById('menu-bar').children;
  for(let bar of menuBars) {
     const id = bar.getAttribute('id');
     bar.addEventListener('click', menuButtons[id]);
  }
}

setupEditor();

L0编辑器痛点

上面通过浏览器contentEditable + document.execCommand实现了一个简单的富文本编辑器,这种编辑器存在很多坑。不用浏览器厂商对contentEditable实现方案不同,导致contentEditable中生成的Dom结构不可控。 例如通过Enter回车创建一个新的文本行,预期是换行。但是在不同浏览器中新的一行html标签各不相同。Chrome/Safari以及Firefox60+中是div标签,IE中是p标签。还有很多这种类似问题,这里就不展开说明啦。

相关推荐
没资格抱怨6 分钟前
vue3中利用路由信息渲染菜单栏
前端·vue.js
TttHhhYy9 分钟前
vue写后台管理系统,有个需求将所有的$message消息提示换成确认框来增强消息提示效果,遇到嵌套过多的情况,出现某些问题
前端·javascript·vue.js·anti-design-vue
FIRE40 分钟前
uniapp小程序分享使用canvas自定义绘制 vue3
前端·小程序·uni-app
四喜花露水41 分钟前
vue elementui el-dropdown-item设置@click无效的解决方案
前端·vue.js·elementui
jokerest1231 小时前
web——sqliabs靶场——第五关——报错注入和布尔盲注
前端
谢尔登1 小时前
前端开发调试之 PC 端调试
开发语言·前端
每天吃饭的羊1 小时前
在循环中只set一次
开发语言·前端·javascript
_默_4 小时前
adminPage-vue3依赖DetailsModule版本说明:V1.2.1——1) - 新增span与labelSpan属性
前端·javascript·vue.js·npm·开源
也无晴也无风雨6 小时前
深入剖析输入URL按下回车,浏览器做了什么
前端·后端·计算机网络
Martin -Tang7 小时前
Vue 3 中,ref 和 reactive的区别
前端·javascript·vue.js