为插入格式化文本添加更好的撤销支持

在 Discourse 中,如果您将纯文本粘贴到编辑器中,浏览器通常的行为(通过 Ctrl+Z)会撤销粘贴并删除粘贴的文本。但是,如果您粘贴格式化文本(例如此粗体文本),则撤销不起作用。同样,无法撤销在文本上粘贴链接以创建链接,也无法撤销通过快捷方式(如 Ctrl+B)或从编辑器工具栏选项添加格式时插入的 Markdown。理想情况下,应将尽可能多的这些内容添加到撤销堆栈中,以便 Ctrl+Z 能够正常工作。

让我描述一下我经常遇到的情况。

  1. 我从另一个网站复制了一些文本,该文本碰巧是一个链接、粗体、标题等。(有时会识别此事实,有时则不会。)
  2. 我进入 Discourse,将此文本粘贴到我已部分写好的帖子中。
  3. 我按下 Ctrl+V,它会插入格式化文本。
  4. 我意识到我的错误,按下 Ctrl+Z,但没有任何反应。
  5. 我手动删除我错误插入的格式化文本。(大多数情况下,是我错误粘贴的链接版本,所以不像我只需要删除“#”之类的东西。)
  6. 我按下 Ctrl+Shift+V,这是我第一次应该做的事情,以粘贴未格式化的文本。

显然,这部分是用户错误,当我没有搞砸时,只需要两个步骤(从其他网站复制,在 Discourse 中粘贴为纯文本)。但当我搞砸时(这很常见,因为我习惯于直接按 Ctrl+V,因为大多数网站不会进行格式化粘贴),如果我可以通过 Ctrl+Z 正常工作来节省一些时间,那将是很好的。

4 个赞

这通常是浏览器的原生行为,并不是 Discourse 在执行。尝试在纯文本框的 HTML 中进行测试。

好的,默认情况下,当你使用 JavaScript 修改输入时,撤销功能是无效的。但我搜索了一下,发现 document.execCommand 可以在插入文本的同时将其添加到撤销堆栈中。

例如,如果你执行 document.getElementById('myInput').value = 'asd',然后按 Ctrl+Z,它将不会撤销。

但如果你在光标位于你想要的位置(根据当前的 Discourse 工作流,应该就是这样)执行 document.execCommand('insertText', false, 'asd'),文本将被正确插入,并且 Ctrl+Z 将按预期删除添加的文本。

基本上,我想知道 document.execCommand(或者如果认为有其他方法更好,则使用其他方法)是否可以用于将内容追加到撤销堆栈,以便在这些情况下 Ctrl+Z 可以正常工作。

3 个赞

不 — 我们多年前就特意从 Discourse 中移除了该支持,以便让浏览器原生文本框的撤销功能按预期在所有标准网站上正常工作。

我认为我一定遗漏了什么。“让浏览器原生文本框的撤销处理工作”具体是指什么?据我所知,在 Discourse 中,格式化文本的撤销根本不起作用,所以您是说撤销不起作用 就是 标准网站行为吗?

我之所以感到困惑,是因为除了像 Microsoft Word 这种撤销功能可以正常工作的网站之外,我想不出任何其他网站支持格式化粘贴(除了 Discourse)。因此,我没有任何可以与 Discourse 进行比较以了解什么是“标准”的。如果您能指出一些可供比较的网站,那将非常有帮助。

2 个赞

请参阅

按“Try it yourself”,然后在文本框中输入文本,暂停一下,然后按 Ctrl+Z 来撤销您的操作。这是一个演示。首先,我们按“Try it yourself”按钮,这会在浏览器中显示一个 HTML <textarea>

我在文本框中输入了一些文本。正如您在屏幕截图中看到的,我输入了

我刚输入了这段文字,太棒了!

现在,输入后,我按 Ctrl+Z 来撤销我的输入,我看到这个:

请注意,文本已恢复到其先前的状态,这完全由浏览器本身处理,不涉及任何 JavaScript 代码。

我的理解是 @seanblue 正在询问我们是否可以修改操作 textarea 时使用的浏览器 API,以便更好地提示浏览器,使其能够更好地处理撤销。因此,这只适用于键盘快捷键、工具栏、上传等。

我并不反对在这里调整一些东西,但我担心其中一些 API 需要非常小心,肯定有回归的风险。

我不反对在这里进行实验,也许社区可以发送一些 PR 来向我们展示如何操作。

7 个赞

我们可以在这方面做得更好。我们工具栏上的许多按钮和粘贴行为都是直接在 JavaScript 中设置文本区域的值。这完全破坏了浏览器原生的撤销/重做历史记录。

相反,每当我们以编程方式修改文本区域时,我们都应该使用 document.execCommand(正如 @seanblue 所提到的)。这样,浏览器就会将其解释为用户操作,并将其干净地插入到撤销/重做历史记录中。

insertText 命令,您可以使用它以编程方式替换光标处的文本,同时在纯 textareainput 元素中保留撤销缓冲区(编辑历史记录)。

10 个赞

但是那个文本框不支持富文本,我说的不是那个。我知道浏览器可以正常撤销输入和粘贴的文本。我的意思是 Discourse 不支持撤销粘贴的富文本。请按照以下步骤操作,看看我指的是什么。

首先,打开 Discourse 的编辑器:

现在复制以下文本并粘贴到编辑器中:this is a test

现在按 Ctrl+Z,粘贴的文本就会被删除。这与你在帖子中演示的行为完全相同。

现在改复制以下文本并粘贴到编辑器中:this is a great test
请注意,它会粘贴带有斜体化“great”的 markdown。

现在按 Ctrl+Z,你会注意到粘贴的文本仍然在那里。这就是我一直在说的。


没错。我并不是建议你自己用 JavaScript 来处理撤销。我建议的是,在操作 textarea 时,让浏览器知道,这样当用户按下 Ctrl+Z 时,它就可以撤销更改。

就我而言,如果粘贴富文本后 Ctrl+Z 可以正常工作,我 99% 的不满就会解决。如果每一个操作都可以用 Ctrl+Z 撤销,那是否是理想情况?当然。但大多数其他操作都可以通过重复原始操作来撤销(例如,按 Ctrl+B 可以添加和删除粗体 markdown)。但粘贴时,它可能会包含大量意外的 markdown,包括标题、链接,甚至表格,这就是为什么在这种情况下撤销如此重要。

如果我们只处理富文本粘贴的撤销情况,而忽略其他快捷键、工具栏按钮等,这是否能足够降低风险,让我们尝试一下?

9 个赞

我明白了;在您和 @david 的解释之间,我理解了其中的区别。我只是在大多数情况下从不真正使用编辑器中的那些按钮。我使用电脑键盘(物理或屏幕上的)在文本框中输入内容,这由浏览器无缝处理。

6 个赞

这可以理解。我也倾向于手动输入 markdown 而不是使用工具栏按钮,所以这方面对我来说不是问题。我之所以在 OP 中提到工具栏,只是为了指出它不仅仅在粘贴格式化文本时发生。能够撤销工具栏操作并不是非常重要,因为用户是故意执行这些操作的。但粘贴时,格式化通常是偶然且出乎意料的,因此能够撤销它会非常方便。

6 个赞

我想跟进一下这件事,看看在可预见的未来有多大的可能性会着手处理。

3 个赞

尚未安排,但是的,它似乎能奏效。我认为我们应该更改工具栏和类似 CTRL-B 快捷键以及提及功能的实现。

不过,这是一个相当大的改动,我认为需要大约 1-3 周的工作才能全部实现。涉及的方面很多:

  • 剪切和粘贴图片
  • 上传
  • 粗体/斜体
  • 链接
  • @提及
  • #自动完成

我支持这项更改,只是不确定何时可以安排……我想我可以在下一个版本中安排它,@codinghorror 有什么异议吗?

我确实喜欢你可以一直按 CTRL-Z 回到空白框,而不是在第一次提及、链接等时被阻止……

8 个赞

一个好处是(在我看来)它可以合理地逐步完成,而不是一次性发布。当然,我从技术角度不知道是否会这样,但从用户角度来看,我认为没问题。一些逻辑上的分离可能包括:

  • 粘贴会变成格式文本,包括粗体、图片和链接等内容
  • 自动完成提及、类别/标签、表情符号
  • 键盘快捷键,如 Ctrl+B 粗体
  • 工具栏操作,如粗体、隐藏细节、模糊剧透等。

我觉得每一组都可以单独完成而不会让用户感到困惑,而且我个人会按这个顺序实现。

8 个赞

我将把这项工作安排在我们下一次发布中,这意味着我们将在未来六个月左右的时间里完成这项工作,这不会一蹴而就,但我们会取得进展。

8 个赞

既然已经过去四个月了,我想跟进一下,看看进展如何。:slight_smile:

2 个赞

是的,我完全理解你的意思,但这还需要一段时间。

它已经演变成需要对编辑器进行彻底重构。我们的长期计划是为编辑器支持不同的抽象层,而现在它被超级紧密地绑定为始终是 TEXTAREA 元素。

第一个解除阻塞的举措是支持一个内容可编辑的编辑器,它看起来和行为都像我们当前的文本区域。

我认为我们还需要 3 个月才能开始这个项目,因为我们还有 3 个非常大的项目排在它前面,但我确实认为我们今年会开始这个项目。

2 个赞

没关系,我只是想了解一下最新进展。

哇,我以前从未听说过 contenteditable。您介意简要解释一下为什么需要/想要进行此更改吗?如果不方便也没关系,我只是好奇。

这有点复杂,但我们确实希望进入富文本编辑器实验的世界。这将解锁它。

这项工作之所以处于关键路径上,是因为我们所有的内部机制都与特定的实现(TEXTAREA)紧密耦合。我们没有一个单一的函数来与组合器接口,它更像是剪切和粘贴 20 个不同的实现。

我们希望做一个小的“骨架”组件,它说明:

  • 如何选择文本
  • 如何插入文本

等等……然后我们可以将骨架重新实现为 contenteditable 或对 TEXTAREA 友好的撤销实现。

但是,需要移动大量的代码来实现这一点。

2 个赞

我在这里取得了一些进展:

这远没有达到 @sam 所描述的长期工作的全面程度。但我认为这在短期内会有所帮助。这应该能在粘贴富文本、引用以及使用(大多数)编辑器按钮/键盘快捷键时保留撤销历史记录。

它现在已经在 Meta 上上线了——如果您发现任何问题,请大声疾呼。

3 个赞