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

摘要:先前我们主要处理了浏览器复杂DOM结构的默认行为,以及兼容IME输入法的各种输入场景,以此需要针对性地处理输入法和浏览器兼容的行为。在这里我们关注于处理文本结构性变更行为的处理,主要是针对行级别的操作、文本拖拽操作等,分别处于文本结构结构以
先前我们主要处理了浏览器复杂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-基于组合事件的半受控输入模式 从零实现富文本编辑器#8-浏览器输入模式的非受控DOM行为 从零实现富文本编辑器#9-编辑器文本结构变更的受控处理 概述 在当前我们主要聊的编辑器输入模式中,主要是关注于文本的半受控输入以及脏DOM的检测,输入状态同步是比较复杂且容易出问题的地方。而在这里我们则关注于输入同步行为扩展,例如回车、删除、拖拽文本等操作,相当于完善了编辑器整体输入模式的处理。 具体来说,执行换行和删除回车时会变更DOM结构,而删除文本以及拖拽文本同样是由BeforeInput等事件组合执行的,在编辑器中这些操作都是输入的一部分。此外,这些操作通常都是可以受控处理的,因此并不太容易出现脏DOM的问题,但是整体上还是有很多需要注意的点: 回车操作通常需要拆分当前行结构,并且还需要关注到行格式的继承问题,特别是类似于列表等结构处理起来会更复杂一些。此外由于数据结构本身的设计不同,回车的操作实现也会有很大的差异,还有诸如软回车、硬回车等不同的回车类型。 删除操作同样会涉及行结构的处理,即删除回车时需要合并行结构,并且也会受到数据结构本身的影响,删除可能并不会符合操作直觉,因此需要手动校正行格式。并且删除的时候还需要关注Unicode字符的处理,特别是类似于Emoji等符号的删除需要特殊处理。 拖拽操作同样会涉及行结构的处理,而在我们的状态管理中本就是以行为单位进行管理的,因此拖拽行级结构相对会简单,当然实现的交互上还是有些工作量。而文本节点本身同样是可以拖拽的,因此我们同样需要根据选区范围进行文本的剪切和插入。 回车操作 在最开始的时候,我们就聊到了ContentEditable的不受控行为,特别是回车操作在不同浏览器中的表现是不一致的。在之前的例子中,我们就提到了回车操作在不同浏览器中的表现差异: 在空contenteditable编辑器的情况下,直接按下回车键,在Chrome中的表现是会插入<div><br></div>,而在FireFox(<60)中的表现是会插入<br>,IE中的表现是会插入<p><br></p>。 在有文本的编辑器中,如果在文本中间插入回车例如123|123,在Chrome中的表现内容是123<div>123</div>,而在FireFox中的表现则是会将内容格式化为<div>123</div><div>123</div>。 同样在有文本的编辑器中,如果在文本中间插入回车后再删除回车,例如123|123->123123,在Chrome中的表现内容会恢复原本的123123,而在FireFox中的表现则是会变为<div>123123</div>。 其实这些示例其实也写过很多次了,每次提到浏览器的不受控行为都会提到相关的差异,这些默认行为也变成了我们处理状态同步时需要关注的点。而实际上,关于回车的行为本身我们是可以受控处理的,即阻止其默认行为,然后根据当前的选区状态进行行结构的拆分和格式继承等处理。 通常来说,我们可以通过两种方式阻止默认行为,一种是监听BeforeInput事件并阻止其默认行为,另一种是监听KeyDown事件并阻止其默认行为。前者的好处是可以直接获取到事件的输入类型,例如软硬回车等,而后者的好处则是可以更早地阻止默认行为。 那么自然的我们还是借助BeforeInput事件来处理回车操作,这样会比较方便一些。
阅读全文