execCommand()现在已经过时,有什么替代方案吗?

258

我原本打算使用Document.execCommand()方法以及contenteditable属性来构建自定义的所见即所得编辑器,但当我查看Document.execCommand()文档时,发现它已经过时了。那么现代化(或现存的)替代方案是什么?


1
看看这个:MDN: 让内容可编辑 - 尽管它似乎也涉及到 document.execCommand()... - Peter B
2
还有一个事实是,contenteditable 是一个相当不可预测的东西,所以您可能要考虑根本不使用 contenteditable,而是将元素替换为具有该元素内容预加载的编辑器。 - Mike 'Pomax' Kamermans
4
如今使用Vue、React等技术,编写一个可以将用户点击的元素“替换”为代码文本框 (普通文本框、Draftail、CodeMirror等),以便进行编辑,并在失焦时将更新后的内容“替换”回原始元素,这已经成为一件微不足道的事情。甚至不需要使用框架,只需用原生JS编写元素交换代码即可,这样会比contenteditable提供更多控制。 - Mike 'Pomax' Kamermans
1
有什么需要解释的呢?如果你想在Vue或React中编写编辑器,谷歌搜索仍然可以正常工作,并且有很多文章解释如何做到这一点。然而,链接到任何特定的文章在那个链接不可避免地变成404时将无法帮助未来的用户。(这就是为什么要求教程等内容是离题的原因:今年有效的方法可能明年已经失效,或者可能已经成为过时技术) - Mike 'Pomax' Kamermans
显示剩余4条评论
9个回答

94
2022年的答案是: execCommand() 已正式废弃,但没有替代方法。所以,如果你必须拥有丰富文本支持,你只能继续使用 execCommand() 并自己找出适用于要支持的浏览器的有效方法。
请注意,实际上的用户代理(例如 Chrome、Firefox 和 Safari)不能取消对 execCommand() 的支持,因为许多服务需要其支持。事情的可悲状态是,HTML5 无法指定任何通用基础。这是因为浏览器供应商对于 execCommand() 的工作方式没有达成一致意见。因此,在 HTML5 中无法指定任何内容,它的总体目标是为任何新浏览器进入市场而指定所有必需和仅必需的交互操作内容。然而,在我看来,HTML5 在这里失败了,因为任何新浏览器要进入市场,都必须兼容 execCommand()。因此,至少将其中一个浏览器特定实现标准化为官方实现是有意义的。
所有当前的标准化工作(输入事件2、剪贴板API)甚至都没有尝试覆盖 execCommand() 实际执行的功能(例如撤消/重做、在选择范围内实际更改内容)。
最难解决的问题是插入符号移动、选择文本、不同输入法编辑器(IME)的行为和处理本地剪贴板。这些都会以不同的方式与浏览器和操作系统交互。例如,iPad 上使用传统中文书写时 iOS Safari 的行为与 Windows 上使用泰语 IME 的 Firefox 完全不同。这又完全不同于在 Chromebook 上运行时书写芬兰语或输入具有多个 UNICODE 码点编码的表情符号时的 Chrome 行为。由于没有一种可用的 JS API 公开了所有细节,因此必须对大多数浏览器使用 contenteditable 和可能还需要使用 execCommand() 才能从 IME 中获取正确的输入。
另外请注意,例如 Android GBoard 需要了解同一可编辑内容中周围的文本,因为它使用 AI 逻辑来正确地确定向用户提供哪些单词建议,以便输入文本。因此,您不能通过插入符号下的单个空不可见文本区域来欺骗它,因为这将缺少所需的上下文。尽管如此,这并没有阻止人们试图做到这一点,但实际应用中失败了。
这种情况已经持续了五年以上,因此不要期望任何快速的变化。

2
看起来当前的“计划”是,需要富文本编辑器的应用程序应该监听 beforeinput 事件,从事件中收集待完成的修改信息并阻止浏览器默认行为,而是在 JS 代码中执行自定义内容修改(自定义富文本编辑器逻辑),然后使用 HTML+CSS 渲染结果。这需要在 JS 中实现所有内容,包括撤销/重做和所有操作,因此不需要任何浏览器支持。 - Mikko Rantalainen
2
背景信息请参考:https://medium.com/content-uneditable/fixing-contenteditable-1a9a5073c35d - Mikko Rantalainen
1
在你随意使用 beforeinput 之前,请先阅读此文:https://medium.com/content-uneditable/contenteditable-the-good-the-bad-and-the-ugly-261a38555e9c - Mikko Rantalainen
3
@aKiRa,你能提供更多的细节吗?根据 https://caniuse.com/?search=execcommand 的信息,它仍然受支持。 - Mikko Rantalainen
1
2023仍然如此 - dungmidside
显示剩余3条评论

86

我为我的平台创建了一个丰富的编辑器,用于XML(HTML5 + XHTML)编辑目的。我不会说document.execCommand()已经完全死亡,因为它的某些部分仍然可以正常工作。不幸的是,对我来说,主要问题是浏览器使用许多不同的代码来生成那些屏幕阅读器无法识别的样式,这些阅读器被盲人或近乎盲人使用。

此外,我曾经遇到过最昂贵的时间错误是Gecko/Presto错误,其中视觉和技术选择(为什么它们不是同一件事,请不要问我)将导致DOM的某些部分被更改,而用户并没有打算这样做,这归结于每个字符的像素计数较低,因此如果Rich Editor不尊重视觉选择,用户很快就会离开。这需要四个月的时间才能克服,还有其他的错误。

最终,这是一个艰苦但可实现的努力,如果你打算像我一样构建一个HTML/XML编辑器,你应该计划至少六个月的时间,如果你不仅要做得正确,而且要测试到讨厌蛋糕的程度,然后只有有人指出另一个错误。

你的JavaScript方面的主要重点应该是以下内容:

使用execCommand()从不同的浏览器生成的代码不一致(通常设置内联样式,如果不彻底否定它,则会使您网站的CSS变得复杂),您应该坚持使用以下元素,您不仅可以控制,而且还与屏幕阅读器兼容:

  • em 用于强调文本(或“斜体”,<i>已被弃用)。
  • strong 用于强烈的文本(或“粗体”,<b>已被弃用)。
  • u 用于下划线(确保您的锚点样式与元素不同;u可能被认为是“已弃用”的,但当我在未来十年左右修复标准时,我会反转这一点,适当使用它)。
  • sub 用于垂直低于正常文本的子行文本。
  • sup 用于垂直高于正常文本的上行文本。
  • 请勿使用元素特别添加这些样式,因为屏幕阅读器将无法理解或显示错误行为;当适当使用时,它仍然是一个有效的通用内联元素。

我一直打算修改我的富文本编辑器 (虽然它已经得到了修补,但还没有被彻底重写),但你可以在我个人资料中链接的网站上加载博客页面时查看源代码。原始项目花费我11个月的时间,但是现在有了更多的经验,我认为只需要三到四个月的时间就可以完成。如果你是认真的,我强烈建议远离框架和库。"但是…但是,他们会使我的生活变得更轻松!" 直到你想使用新版本并不得不重写整个项目。第一次使用纯 JavaScript 并避免无意义的维护。祝你好运!


2021年9月24日:我最终重新开始了大约一年前的Rich Editor II工作,并成功将改变样式的代码从100,515个字符转换为约6,000个字符,并将文件请求(压缩后的有效带宽)减少了三分之一。以下是成功的关键部分:
  1. anchorNodefocusNode根据从左到右或从右到左选择而切换。因为我在我的平台上找不到任何理由说明这很重要,所以我将an对象(代表anchorNode)放在左边,将fn对象(代表focusNode)始终放在右边。

  2. 我使用了约1,700个字符来解决Gecko/Presto问题;您可以先访问一个具有丰富表单的页面,在网站上找到它。

  3. 为了解决通过<s><sub><sup><u>等多种交换选择的问题(您必须测试简单和非常复杂的示例),我最终使用了window.getSelection().getRangeAt(0).commonAncestorContainercloneNode,然后在处理之前剥离了未包含在选择中的内容。然后,我只需使用window.getSelection().deleteFromDocument();删除选择,并通过document.createElement创建新的样式元素,我可以轻松地将其appendChild到选择中,并将其插入到window.getSelection().getRangeAt(0).insertNode(id_('editor_rich_text').firstChild);

Gecko浏览器(例如Waterfox,Pale Moon以及现在已完全摧毁的Firefox)允许您选择多个文本实例。要这样做,只需按住Control键来创建额外的选择。由于它并没有真正有任何意义的帮助(而且这些东西已经够复杂的了),我没有特别支持它。

我将在今天或本周末更新我的平台(根据此帖子的编辑时间)以反映新的更改。由于Gecko浏览器存在许多问题,我仍保留了许多旧代码。我扩展了功能,并解决了许多错误,而且没有采用任何黑客方式,也没有使用任何车库(框架或库)。


2021年9月26日:对于那些对撤销/重做功能感兴趣的人,你们将不得不使用基本上保留正在编辑的DOM部分的文本版本。当然,也可能有其他实现方式,但这些方式会非常复杂。你只需要使用 .cloneNode 制作编辑器父元素的副本,然后在内存中使用类似于 while(e.firstChild){xml + = new XMLSerializer().serializeToString(e.firstChild);} 的东西。由于您将其存储为文本,因此它不会具有DOM所具有的巨大内存影响。您将完全替换编辑器中的整个DOM,并跟踪每次更改的每个迭代,因此它仍将是一个庞大的项目。对于我的平台来说,现在并不是必要的,但我想提一下这一点,因为有些人在评论中提到了它。


啊 - 谢谢!所以我应该偏爱最精确的文本,例如 ustrong 等等,而不是 span?我的意思是… 屏幕阅读器(如 JAWS)会解释 aria 属性,就像我在开发应用程序时记得那样,这些属性也必须能够被这些程序读取。对,嵌套是真正的麻烦所在……所以我想我也不应该使用 contentEditable 了吧? - Igor
@Igor 我的网页平台仍然使用原始的Rich Editor(您可以通过评论在博客主题页面上预览,除非需要,否则不会加载,按需JavaScript)。contentEditable属性是有效的。至于处理复杂嵌套,有两件事要做:1.为最大复杂度设置策略,并在超过时指示用户使用XML编辑器;2.确定最大阈值,使您的项目发布变得现实。如果您决定在复杂性方面走极端,请模仿LibreOffice的行为;我认为它目前是处理这种复杂性的“标准”。 - John
2
我可能错了,但是execCommand可以免费提供撤销/重做功能。如果我们不使用execCommand,有什么好的替代方案呢? - Chuanqi Sun
1
@ChuanqiSun 我目前最好的想法是使用data-edit或类似的非侵入式HTML/XML属性来跟踪更改,并根据其相对位置和/或先前元素的内容分配值。撤消/重做是有序执行,因此您只需在JavaScript/CSS属性选择器上向前或向后迭代即可。由于我正在慢慢重写我的富文本编辑器,所以我还没有达到那个点。但是,如果能够胜任,您可能需要查看大约800-1,600字节的JavaScript。 - John
1
代码在哪里?你能提供GitHub链接吗?这个网站太慢了。 - Niket Tongare
显示剩余2条评论

45

您可以使用以下内容:

navigator.clipboard.writeText('text to be copied');

它执行与document.execCommand("copy")完全相同的操作。


2
问题在于当您需要编写更多内容时,例如HTML时,execCommand可以为您提供将事件侦听器附加到复制方法的可能性,该侦听器获取ClipboardEvent对象,这更加有用。 - Adrian Stanisławski
2
@AdrianStanisławski 是的,他们没有完全替换execCommand,这真是太糟糕了。 - Ahmad Othman
1
另一个问题是.clipboard需要https。所以我编写了一个执行命令来复制,可以在非https区域中工作。execCommand是解决非https故障的临时方法。但是……对于execCommand,有什么解决方法呢? - Watercayman
@Watercayman 看来我们永远不会知道了,虽然有一些解决方案,但它们相当奇怪或愚蠢。 - Ahmad Othman
它只能在Chromium上运行。 - undefined

26

看起来替代方案将是Input Events Level 2

虽然自己制作所见即所得编辑器非常诱人,但我个人会坚持使用 execCommand 直到新的标准推出。因为我们都只是会自己编写一个完整的编辑器,而不是重用现有的工作解决方案。


1
请注意,输入事件2似乎仅在用户与“contenteditable”交互时提供自定义JS代码的事件。就我所见,它不能用于修改编辑内容的状态。剪贴板API似乎存在类似的问题,它提供的是有关剪贴板的更多信息,而不是实际可以修改可编辑内容的功能。 - Mikko Rantalainen
5
完全同意。现在已经是2022年了,execCommand仍被视为不推荐使用,而且仍有计划替换它以提供相同的功能。即使提出了一些建议(似乎不是很有用),但在6年多的时间里似乎并没有进展。与此同时,所有主要浏览器(至少根据CanIUse.com的数据有15个)都继续支持execCommand。 - Iain Collins
2
@IainCollins 浏览器供应商实际上没有选择,因为他们不能在等待明智的规范时从当前的“贫瘠的富文本编辑”转向“无富文本编辑”。我很失望,他们没有将execCommand()的合理部分标准化,以便清晰地前进。 - Mikko Rantalainen

9
document.execCommand() 的替代方案是 Clipboard API,通过 navigator.clipboard 访问。根据 MDN Web Docs(https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API):

此 API 旨在取代使用 document.execCommand() 访问剪贴板的方式。

编辑:如上面引用的文字所述,这只满足一部分需求(访问剪贴板)。


37
如何使用剪贴板API加粗文本、撤销/恢复更改等操作?这只能满足execCommand的极小子集。 - Kaiido

8
这里的结论是,尽管w3CexecCommand()发出了警告,但它仍然可用,并且直到2022年现在仍没有标准替代方法,除非使用JavaScript版本库。

1
你能列举一些这样的库吗? - aderchox

5

6
“contentEditable”规范在2015-2020年间进行了修改,最终的结果是零行规范:https://w3c.github.io/contentEditable/。 我不会指望这个规范很快能够完成。 - Mikko Rantalainen
2
补充一点,现在已经是2022年了,我们似乎仍然没有更接近替换execCommand的方案,甚至那些模糊提出的解决方案也没有尝试解决同样的问题。 - Iain Collins

0

使用编程语言可以制作所见即所得编辑器。

window.getSelection()

但现在继续使用execCommand也不是问题。


-1

document.execCommand 没有问题。问题在于函数底层使用的 HTML 4 标准不再被广泛支持。例如,FontSize 命令使用已经被弃用的 <font> 标签。但是也有好消息。第一个好消息是 document.execCommand 从 javascript 中将永远不会被移除。第二个好消息是 - 你可以使用 insertHTML 命令来插入任何你想要的 HTML。

function FontSize40px(){
   var spanString = `
   <span
      style="font-size: 40px;"
   >${ document.getSelection().toString()}
  </span>
   `
   
   document.execCommand('insertHTML', false, spanString);
}

这只是一个愚蠢的例子 :))


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接