实现一个富文本编辑器是一个复杂的项目,涉及到前端和后端的多个方面。以下是一个简化的实现方案,我们将使用HTML、CSS和JavaScript来构建一个基本的富文本编辑器。### 1. HTML结构首先,我们需要一个容器来放置编辑器:```htmlRich
摘要:先前我们提到了,数据模型的设计是编辑器的基础模块,其直接影响了选区模块的表示。选区模块的设计同样是编辑器的基础部分,编辑器应用变更时操作范围的表达,就需要基于选区模型来实现,也就是说选区代表的意义是编辑器需要感知在什么范围内执行变更命令。
先前我们提到了,数据模型的设计是编辑器的基础模块,其直接影响了选区模块的表示。选区模块的设计同样是编辑器的基础部分,编辑器应用变更时操作范围的表达,就需要基于选区模型来实现,也就是说选区代表的意义是编辑器需要感知在什么范围内执行变更命令。
开源地址: https://github.com/WindRunnerMax/BlockKit
在线编辑: https://windrunnermax.github.io/BlockKit/
项目笔记: https://github.com/WindRunnerMax/BlockKit/blob/master/NOTE.md
从零实现富文本编辑器项目的相关文章:
深感一无所长,准备试着从零开始写个富文本编辑器
从零实现富文本编辑器#2-基于MVC模式的编辑器架构设计
从零实现富文本编辑器#3-基于Delta的线性数据结构模型
从零实现富文本编辑器#4-浏览器选区模型的核心交互策略
浏览器选区
数据模型设计直接影响了编辑器选区模型的表达,例如下面的例子中Quill与Slate编辑器的模型选区实现,其与本身维护的数据结构密切相关。然而无论是哪种编辑器设计的数据模型,都需要基于浏览器的选区来实现,因此在本文中我们先来实现浏览器选区模型的基本操作。
// Quill
{ index: 0, length: 3 }
// Slate
{
anchor: { offset: 0, path: [0, 0] },
focus: { offset: 3, path: [0, 0] }
}
实际上选区这个概念还是比较抽象,但是我们应该是经常与其打交道的。例如在鼠标拖动文本部分内容时,这部分会携带淡蓝色的背景色,这就是选区范围的表达,同样我们可能会将其称为选中、拖蓝、选中范围等等,这便是浏览器提供的选区能力。
除了选中文本的蓝色背景色外,闪烁的光标同样是选区的一种表现形式。光标的选区范围是一个点,或者可以称为折叠的选区。光标的表达通常只出现在可编辑元素中,例如输入框、文本域、ContentEditable元素等。若是在非可编辑元素中,光标处于虽然不可见状态,但实际上仍然是存在的。
浏览器选区的操作主要基于Range和Selection对象,Range对象表示包含节点和部分文本节点的文档片段,Selection对象表示用户选择的文本范围或光标符号的当前位置。
Range
Range对象与数学上的区间概念类似,也就是说Range指的是一个连续的内容范围。数学上的区间表示由两个点即可表示,Range对象的表达同样是由startContainer起始到endContainer结束,因此选区必须要连续才能够正常表达。Range实例对象的属性如下:
startContainer:表示选区的起始节点。
startOffset:表示选区的起始偏移量。
endContainer:表示选区的结束节点。
endOffset:表示选区的结束偏移量。
collapsed: 表示Range的起始位置和终止位置是否相同,即折叠状态。
commonAncestorContainer:表示选区的共同祖先节点,即完整包含startContainer和endContainer最深一级的节点。
Range对象还存在诸多方法,在我们的编辑器中常用的主要是设置设置选区起始位置setStart、设置结束位置setEnd、获取选区矩形位置getBoundingClientRect等。在下面的例子中,我们就可以获取文本片段23的位置:
<span id="$1">123456</span>
<script>
const range = document.createRange();
range.setStart($1.firstChild, 1); // $1
range.setEnd($1.firstChild, 3);
console.log(range.getBoundingClientRect());
</script>
获取文本片段矩形位置是个非常重要的应用,这样我们可以实现非完整DOM元素的位置获取,而不是必须通过HTML DOM来获取矩形位置,这对实现诸如划词高亮等效果非常有用。此外,需要关注的是这里的firstChild是Text节点,即值为Node.TEXT_NODE类型,这样才可以计算文本片段。
既然可以设置文本节点,那么自然也存在非文本节点的状态。在调用设置选区时,如果节点类型是Text、Comment或CDATASection之一,那么offset指的是从结束节点算起字符的偏移量。
