要在 web 页面上实现富文本格式的编辑功能,向来不是一个容易的事情,大部分常见的富文本编辑器都是基于 domcontenteditable 来构建的。

基于 contenteditable, selection / rangedocument.execCommand 的编辑器实现,是目前广泛流传的 contenteditable is terrible1 的由来。

此种方案主要有以下两个方面的问题:

  • 输入处理不一致。确定的输入,不能产出确定的输出,产生的 HTML 是不一致的。
    • execCommand、按键等的处理,具有浏览器差异性。
    • 表现和内容是脱离的。如这两种 html 结构 <strong><u>test</u></strong><u><strong>test</strong></u>,都是粗下划线的文字,但是数据截然不同。
  • range 光标。光标在页面上表现为一个闪动的竖线,让用户知道当前的输入位置是在哪里。range 的位置和 dom 结构有关系,也和用户的点击有关系,想象一下这两种光标的位置 <strong>|<span>text</span></strong><strong><span>|text</span></strong>

此外,对于一个优秀的富文本编辑器来说,还需要提供良好的扩展性,方便使用者进行功能的扩展,诸如添加新模块、修改工具栏、制定自定义的过滤规则等。

针对 contenteditable 所存在的问题,kix(google doc 的编辑器) 采取了自绘光标和模拟输入的形式来解决,此外还有一些基于 canvas 的方案。


Tea.js 的方案

  • 基于 contenteditablecontenteditable 提供了良好的输入功能,能够较好地兼容各种 IME 输入 (特别是 cjk),即 contenteditable 只作为 input 层。
  • dom view 作为纯 view 展示层,不处理任何 dom 操作和更新。
  • 劫持 input 输入,解析后转化为对 command 的调用,操作 model 树。
  • 提供文档数据模型层 model,将对 dom 的操作映射到对文档数据模型层 model 的操作,input -> model change -> view update
  • 实时修正 range 位置,确保每一次 selection change 的光标位置都是可预测的。
  • 基于可插拔组件的架构设计,方便扩展。

参考资料

  1. why-contenteditable-is-terrible