实现一个富文本编辑器是一个复杂的项目,涉及到前端和后端的多个方面。以下是一个简化的实现方案,我们将使用HTML、CSS和JavaScript来构建一个基本的富文本编辑器。### 1. HTML结构首先,我们需要一个容器来放置编辑器:```htmlRich

摘要:在先前我们实现了编辑器选区和模型选区的双向同步,来实现受控的选区操作,这是编辑器中非常重要的基础能力。接下来我们需要在编辑器选区模块的基础上,通过浏览器的组合事件来实现半受控的输入模式,在这里我们需要处理浏览器复杂DOM结构默认行为,还需要
在先前我们实现了编辑器选区和模型选区的双向同步,来实现受控的选区操作,这是编辑器中非常重要的基础能力。接下来我们需要在编辑器选区模块的基础上,通过浏览器的组合事件来实现半受控的输入模式,在这里我们需要处理浏览器复杂DOM结构默认行为,还需要兼容IME输入法的各种输入场景。 开源地址: https://github.com/WindRunnerMax/BlockKit 在线编辑: https://windrunnermax.github.io/BlockKit/ 项目笔记: https://github.com/WindRunnerMax/BlockKit/blob/master/NOTE.md 从零实现富文本编辑器项目的相关文章: 深感一无所长,准备试着从零开始写个富文本编辑器 从零实现富文本编辑器#2-基于MVC模式的编辑器架构设计 从零实现富文本编辑器#3-基于Delta的线性数据结构模型 从零实现富文本编辑器#4-浏览器选区模型的核心交互策略 从零实现富文本编辑器#5-编辑器选区模型的状态结构表达 从零实现富文本编辑器#6-浏览器选区与编辑器选区模型同步 从零实现富文本编辑器#7-基于组合事件的半受控输入模式 编辑器输入模式 Input模块是处理输入的模块,输入是编辑器的核心操作之一,我们需要处理输入法、键盘、鼠标等输入操作。输入法的交互处理是需要非常多的兼容处理,例如输入法还存在候选词、联想词、快捷输入、重音等等。甚至是移动端的输入法兼容更麻烦,在draft中还单独列出了移动端输入法的兼容问题。 编辑器输入模块与选区模块类似,都需要在浏览器DOM的基础上处理其默认行为,特别是需要唤醒输入法的输入则需要更多模块的联动,因此还需要复杂的兼容性适配。而输入模式本身则分为三种类型,即非受控输入、半受控输入和受控输入,每种输入模式都有其特定的使用场景和实现方式。 非受控输入 非受控的方法,指的是完全依赖浏览器的默认行为来处理输入操作,而不需要对输入进行干预或修改,当DOM结构发生变化后需要收集变更,再应用到编辑器中。这种方式可以最大限度利用浏览器原生能力,包括选区、光标等,然而其最大的问题就是输入不受控制,无法阻止默认行为,不够稳定。 举个目前比较常见的例子,ContentEditable无法真正阻止IME的输入,这就导致了我们无法真正接管中文的输入行为。在下面的这个例子中,输入英文和数字是不会有响应的,但是中文却是可以正常输入的,这也是很多编辑器选择自绘选区和受控输入的原因之一,例如VSCode、钉钉文档等。 <div contenteditable id="$1"></div> <script> const stop = (e) => { e.preventDefault(); e.stopPropagation(); }; $1.addEventListener("beforeinput", stop); $1.addEventListener("input", stop); $1.addEventListener("keydown", stop); $1.addEventListener("keypress", stop); $1.addEventListener("keyup", stop); $1.addEventListener("compositionstart", stop); $1.addEventListener("compositionupdate", stop); $1.addEventListener("compositionend", stop); </script> 采用非受控方法输入的时候,我们需要MutationObserver来确定当前正在输入字符,之后通过解析DOM结构得到最新的Text Model。紧接着需要与原来的Text Model做diff,由此来得到变更的ops,这样就可以应用到当前的Model中进行后续的工作了。 即使是非受控的输入也存在多种实现的方案,例如可以在触发Input事件后以行为基础做文本diff,得到ops后就可以根据schema组合属性。或者也可以完全依赖MutationObserver来得到节点级别的片段变更,在此基础上再做diff,著名的quill编辑器就是如此实现的。 quill针对输入的处理本身并不复杂,虽然涉及到非常多处的事件通信以及特殊case处理,但核心逻辑还是比较清晰的。
阅读全文