如何为 Web 开发文本编辑器
已发表: 2022-03-10我在 Readymag 工作,它开发了一个基于浏览器的设计工具,可以帮助人们创建网站、作品集和各种在线出版物,而无需编码。 我们的工具中有许多小部件可用,文本小部件是使用最广泛的小部件之一。
文本小部件是一个文本输入字段,用户可以在其中使用编辑器中的一系列控件设置文本样式。 每个控件都将 CSS 属性应用于文本。 从用户的角度来看,它看起来就像一个普通的输入文本的字段,但在其看似简单的背后隐藏着大量复杂的过程。
在本文中,我将解释我的公司面临的挑战以及我们用于在应用程序中创建文本小部件的解决方案。 我还将深入探讨我们如何实现它以及我们在此过程中学到了什么——以及在网络上打字的一般工作原理。
在 Web 上编辑文本
有几种方法可以在 Web 上实现文本输入字段。 我们可以使用简单的文本字段、多行textarea
元素或contenteditable
属性来使输入可编辑,或者document.designMode = on
。 它们有何不同?
input
和textarea
元素非常适合向页面添加文本,但它们不提供丰富的文本格式体验。 为此,我们可以使用contenteditable
属性使几乎所有元素都可编辑并启用文本样式。
如果您需要一次编辑整个页面,可以使用document.designMode
。 此模式允许编辑给定文档中的任何元素,甚至是iframe
。
我们选择了contenteditable
属性,它包含所有必要的文本编辑功能。 使用此属性,页面上的任何文本都可以编辑,如果我们想让人们使用 CSS 设置文本样式,这一点非常重要。 例如,用户可以直接设置选定部分或整个文本的样式。
文本样式和字体属性
通过提供对 CSS 开箱即用的所有选项的访问,我们使用户能够以他们希望的任何方式设置文本样式。 除了众所周知的属性(例如字体、样式、颜色和装饰)之外,我们还为用户提供了使用 OpenType 字体功能的机会,例如连字、样式集、分数等。 这些功能通过font-feature-settings
CSS 属性起作用,它允许用户自定义文本样式。
注意:我强烈推荐阅读 Sparanoid 的优秀文章,其中展示了 OpenType 的所有功能。
现代排版向前迈出了一大步,允许通过font-variation-settings
属性在 Web 上使用可变字体。
每个可变字体都有可变属性,您可以更改其值。 例如,在标准字体中,您可以使用严格指定的值( 400
等)更改字体600
,而500
可变字体中,您可以使用可用范围内的任何值,从而提供更广泛的可能性用于文本样式。
.style-1 { font-weight: 600; } .style-2 { font-variation-settings: "wght" 777; }
您可以在下面看到一个示例,说明如何在文本小部件中使用可变字体。
除了注册值( wght
、 wdth
、 slnt
等),字体制作者还可以创建自己独特的字体特征(如上例所示)。 为了让我们的用户有机会使用所有可能的字体功能,我们首先需要此信息。
您要使用的所有功能都应在字体文件中定义。 让我们看看它的规格。 每种字体都可以以表格的形式表示,提供渲染其字符时使用的所有不同信息。
我们使用两个表来收集有关字体的信息:
- 字形替换表
字形替换表 (GSUB) 包含一个字形渲染数据列表。GSUB.featureList
对象是字体特征及其属性的枚举。 您可以在 GitHub 上的表格中查看数据示例。 在这个表中,tag
字段是最有趣的。 这是字体功能的名称,表示此字体可使用此功能。 我们可以安全地在font-feature-settings
属性中使用tag
。 - 字体变化表
字体变体表 (fvar) 是与字体关联的变量属性的表示。 GitHub 上也提供了该表的示例。 每个对象都是一个字体属性,带有对可能值(min
、max
、 default )和本地化名称(如果有)的描述。 我们将这些值与font-variation-settings
属性一起使用。
借助这两个表,我们可以满足所有需求:使用可变字体属性和各种字体特征。 结果数据显示在编辑器的文本小部件控件中,用户可以在其中设置文本样式而无需使用任何代码。
使用键盘
文本输入是文本小部件用户体验的最重要方面之一。 除了启用处理文本的快捷方式外,我们还必须应对一些不寻常的挑战。 使用箭头键导航文本绝对是其中之一。
在用户进行编辑时,文本小部件还会显示隐藏字符,例如不间断空格和换行符。 它们被实现为插入文本中的 SVG 图标,这带来了一个问题:如果我们使用contenteditable
,那么这些图标会阻止用户使用箭头键移动光标。
解决方案很简单:使用span
和:before
伪元素。 这样,浏览器会将图标视为文本,并且箭头键效果很好。
span:before { content: ""; height: 1em; position: relative; background-repeat: no-repeat; background-image: url("data:image/svg+xml,..."); background-position: center bottom; background-size: 1em; }
捷径
在文本小部件中粘贴文本有两个键盘快捷键。
Cmd / Ctrl + V从剪贴板粘贴文本并保留原始文档中的所有样式。 如果文本是从 Pages、Notes、Word 或 Google Docs 等应用程序复制的,那么您的剪贴板将包含 HTML 信息,而不仅仅是纯文本。 可以在保留原始样式的同时解析和粘贴此 HTML。
您可以按如下方式获取 HTML 数据:
// https://www.w3.org/TR/clipboard-apis/#reading-from-clipboard document.addEventListener('paste', (e) => { const text = e.clipboardData.getData('text/plain'); const html = e.clipboardData.getData('text/html'); handlePaste(); });
此外,我们还有Cmd + Shift + V快捷键。 当您使用此键盘组合插入文本时,浏览器会将纯数据留在有效负载中,因此样式由粘贴目标控制。 这些快捷方式默认存在于浏览器中,但您需要记住在您的项目中实现它们。
文本选择和焦点
文本选择可帮助用户查看当前正在编辑的文本。 让我们尝试一个简单的例子:一个带有按钮的输入字段来控制文本的粗细。
在这个例子中,我们可以选择一段文本,然后按下Bold
按钮,之后文本中的选择将保持不变。 但是如果我们的例子更复杂怎么办? 假设我们向文本大小选择器添加了一个输入字段。 在这种情况下,焦点将转移到新的输入。
解决此问题有两种选择:
- 在每个输入事件之后,我们强制焦点移回文本块。 在这种情况下,选择会在一定数量的输入事件后开始闪烁——我们不希望这样。
- 我们可以将文本块添加到
iframe
。 您可能知道,iframe
有自己的全局window
对象。 因此,只要选择在iframe
内,即使外部焦点移动,它也会持续存在。
我们最终得到了一个iframe
包装的文本小部件。 因此,只要选择在iframe
内,即使外部焦点移动,它也会持续存在。 看看下面的截图。 我们在页面上有两个选择:文本小部件中的选定片段和控件中文本大小的选定值。
文本输入期间的性能
文本编辑界面的响应能力很重要。 密切监控每秒帧数 (FPS) 值,尤其是在涉及高速编辑文本或更改字体大小等任务时。
Readymag 有两个视口:桌面和移动。 文本样式可以在每个中显示不同。 在输入文本时,编辑器将执行各种计算以在视口之间同步数据。 通过使用浏览器 API 实现高响应性: requestAnimationFrame
和requestIdleCallback
:
- 每次刷新屏幕时都会调用
requestAnimationFrame
; -
requestIdleCallback
仅在浏览器空闲时调用。
这是在不阻塞主线程的情况下执行繁琐操作的好方法。
可访问性
启用可访问性是当今 Web 开发中最重要的实践之一。 如果您的网站在设计时考虑到了可访问性,它将让更多人访问您的产品。 这意味着不仅要适应残疾人,还要适应不同平台上的用户:桌面和触摸设备、屏幕阅读器、听力设备等。 要了解使项目易于访问的重要性,我建议查看最近的可访问性统计数据。
要开始整合 Web 可访问性实践,请首先查看 Web 内容可访问性指南 (WCAG),这是关于该主题的最全面的资源。 而只要 Readymag 是一个发布工具,除了 WCAG,我们还需要遵循 Authoring Tool Accessibility Guidelines (ATAG)。
我们的团队目前正处于将可访问性集成到编辑器中的阶段。 在随后的文章中,我们将分享更多关于我们在 Readymag 完全集成可访问性的过程。 您还可以使用我们的可访问性清单检查使用 Readymag 所做的任何工作。
最佳实践
最后,这里有一些技巧可以帮助您在 Web 上开发文本编辑器:
- 仔细考虑布局。
提前确定您需要哪些功能以及如何使用文本编辑器中的元素。 - 设置视觉测试。
处理文本时,不能完全依赖快照测试结果。 您可能会在测试中得到正确的结果,期待给定的块 CSS,但有时结果可能不是您所期望的。 - 在不同的浏览器中测试您的工作。
虽然大多数浏览器都很好地支持新的在线功能,但在不同的浏览器中显示相同的样式通常会出现问题。 - 使用功能标志更安全地开发新功能。
- 输入文本时在浏览器中测量 FPS。
不要在单个线程中执行 CPU 密集型任务。 - 不要害怕尝试。
- 最后但同样重要的是,试试 Readymag 中的 Text Widget 。
一些有用的链接
- “OpenType 功能的完整 CSS 演示”,Sparanoid
- “Web 上的可变字体简介”,web.dev
- “令人敬畏的排版,”乔尔·加勒兰
- “可变字体”,尼克·谢尔曼
- 字体包
- OpenType.js