编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

开发小记|富文本编辑器 - 选区

wxchong 2024-06-10 16:35:07 开源技术 43 ℃ 0 评论

由于工作关系,对富文本编辑器有一定研究,借此开个合集做一次沉淀总结。


PS:文中所描述的为L0、L1级别基于contenteditable实现的富文本编辑器,L2自主实现输入区域的不在本文讨论范围内。

第一篇文章选择写「选区」,原因是编辑器的「编辑」实质是选择后修改,而其中的选择操作就是所要介绍的选区在技术层面的实现,可以说是最核心的功能。

本文会先介绍选区的基本概念,再引申介绍光标的实质,最后讲解如何利用选区来实现选择文本设置字号的需求。

选区介绍

选区在具体涉及两个不同的概念,包括了选择「Selection」和范围「Range」

上图的蓝色背景就是范围,所以范围别名也叫「拖蓝」

控制选区本质上就是对范围的操作,因为范围提供了对用户选区的内容进行增删改的能力。选择是范围的集合,如上图所示一个选区可以包含多个范围。

选择提供了 「setBaseAndExtent」、「setPosition」等快捷修改范围的能力,简单场景(例如扩大/缩小用户选择区域)可直接使用选择进行操作。

小技巧:

选择 「toString」 不会包括隐藏元素,但范围会包括一切不可见元素,看个具体的例子。

<!-- "|"代表光标 -->
<p>
  <span>可|见</span>
  <script>不可见</script>
  <span style="display: none">不可见</span>
  <span>可|见</span>
</p>

对上述代码进行选中:

「Selection」的结果是“见可”,而「Range」的结果是“见不可见不可见可”,不可见元素内的文本信息也能被获取。

光标介绍

讲到选区就不得不提光标,光标其实也是一个范围(拖蓝),是起点和终点相同的特殊范围

富文本编辑器很多功能其实都需要在光标处做操作,例如常见的插入一张图片,本质就是先通过获取范围,然后利用 「insertNode」 插入 <img> 标签。

// 当前光标处插入图片
const r = document.getSelection().getRangeAt(0);
r.insertNode(document.createElement('img'));

小技巧:

编辑器中调整字体大小时光标的大小也需要随之调整。但如果在光标处仅仅插入24像素字体大小的 <span> ,光标的高度并不会发生变化,如下图。

<span font15>15号字</span><span font24></span>

这时就要使用「零宽字符」(\ufeff)作为文本节点填充进标签。

<span font15>15号字</span><span font24>\ufeff</span>

但要注意的是,编码中多一个「零宽字符」需要有很多额外的逻辑来保证它的位置正确并且及时被清理,否则在换行、退格时很容易发生格式无法继承的问题,后续会再写篇文章专门讲格式继承(选区合并)。

选择文本调整字号实战

最后我们来利用选区实现一个选择文本并调整字号大小的功能。

<!-- 光标选中c到i -->
<p>ab|c<b>def</b><i>ghi</i>j|kl</p>

如果简单对上述结构的c至j文字调整字号,用以下代码即可实现:

document.execCommand('fontSize', false, 7);

但「execCommand」对字号只有1到7个档位,如果我们希望灵活控制字号大小,就需要精确控制选区内的每个结构。

我们可以利用范围提供的「insertNode」方法在光标的起始和终点插入特殊的标记(就像插入图片一样),插入后我们得到了如下的结构:

<p>ab<span id="start">c ... j<span id="end">kl</p>

接下来就是利用 「nextSibling」 这个属性,从 start 处开始找相邻的文本节点,给文本节点添加字体的样式,直到找到 end 处终止

最后再把插入的标记节点删除即可完成选择文本调整字号的特性,最终的结构如下:

<p>
  ab<font24>c</font24>
  <b><font24>def</font24></b>
  <i><font24>ghi</font24></i>
  <font24>j</font24>kl
 </p>

虽然实现了调整字号的功能,但最终的标签存在了一定程度的冗余,要做得更完善还需要合并结构以及还原拖蓝位置,这部分如何实现就留给读者思考了。

总结

选区是富文本编辑器的基础,包括了「Selection」和「Range」两个概念,并且我们知道了光标的实质也是范围。结尾的示例可以看到利用选区能对用户选中的格式做修改。

「Selection」和「Range」有很多属性、事件和方法,想真正理解它们的特性,最好的方法还是动手编码实践一下。这里推荐一篇文章,里面对每个api做了较详细的描述。

推荐文章:https://zh.javascript.info/selection-range

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表