我的上一篇译文 “[译] 通过 contentEditable 属性创建一个所见即所得的编辑器” 的原文 “Create a WYSIWYG Editor With the contentEditable Attribute” 被本文作者叼了一翻,说会误导吃瓜群众,让初学者误以为富文本编辑器很简单(见原文第一条评论),吓得我赶紧在译文头部郑重申明了一翻。
顺着评论过去看了下这篇文章,虽然有点 CKEditor 软文的嫌疑,但确实有些点值得思考下,遂译之。
不过 CKEditor 确实牛逼,搞了十几年了,编辑器里的老前辈。
本文对于 contentEditable 有点危言耸听,本人观点是基于实际需求出发,如果需要各种格式化功能、甚至是对 Excel/Word 内容的支持,那么使用 CKEditor 这类成熟的框架无疑是非常合适的选择,也是唯一的选择,除非你有拯救世界的梦想;如果开发的只是一个简单的回复功能,只需要支持文本和 Emoji 表情的混排,用框架的确有点重,通过 contentEditable 自己实现也无可厚非,但确实要做好踩坑的心理准备,如果这篇文章可以让你避开一些坑,那就功德无量了。
文中提到的另一篇文章 “Why ContentEditable is Terrible”,有兴趣的可以看下萝卜哥的译文 “【译】为什么web富文本编辑器是天坑?”。
格式说明:链接,原文,说明
原文:ContentEditable — The Good, the Bad and the Ugly
每隔一段时间,就会有一些开发者发现,业界还没有一款完美的网页 WYSIWYG 编辑器,然后就会发生下面的场景:
这简直太奇怪了,嘿,哥们,我们有一点时间,要不自己搞一个?
WYSIWYG 编辑器有什么难的?现在我们已经有了 contentEditable、execCommand() 和 queryCommandState(),只需加个工具栏,放些漂亮的按钮,贴上漂亮的 SVG 或字体图标,点击的时候应用下加粗或者文本链接,简单地排个版,再弄点 CSS 动画,主要工作不就搞定了吗?
剩下的就是一些细节问题了……
怎么让加粗命令使用 而不是 ,按回车键是创建一个新的 而不是插入一个恶心的 在 StackOverflow 上提了几个问题,一个月后,项目里用了一堆好像能解决问题的黑科技(这其实是很可怕的),已经快要死翘翘了。然后开发者气急败坏地加入了 “contentEditable 你个大坑货” 小组,开始对 XYSIWYG 编辑器深恶痛绝。 加粗命令、回车操作和粘贴处理只是冰山一角,但足以让 JavaScript 开发老鸟对 contentEditable 感到厌烦。 我们再来看看如何改变加粗命令的行为,在所有的处理方法中,有一种是通过变化监视器监控编辑器内容的变更,重新标准化 HTML 结构,这是重新实现 execCommand() 行为的一个完美解决方案(在我看来,也是唯一的一个完美方案)。但这里再次强调,这只是冰山一角。 如果使用变化监视器方案,你可能需要处理保留选中区域的逻辑(因为当你修改 DOM 的时候它可能被重置),以及 撤销管理 的逻辑。 如果上面这些都处理好了,那么恭喜,终于搞定了加粗问题。但再想想,回车操作也要这么来一遍,我已经开始怀疑人生了…… 如果你和我一样是一个纯粹主义者,可能会尝试自己实现一个 execCommand(),算法非常简单—— 获取选中内容、获得选中区域(我有提过 Firefox 支持多选区(multi-range selections)吗?)、做些 DOM 处理、重建选区、实现撤销管理(这次可没人帮你做历史记录),搞定! 然而: 够了,我想我已经很好的表达了一个观点:contentEditable 真可怕(ContentEditable is Terrible)。selection、clipboard、drag、drop 等相关 API 以及它们的实现是不完善的,并且(或者)不统一和有各种 bug,Range API 复杂而麻烦。 但冰箱还是空的(工作还得继续),现在你应该知道怎么做了。 既然 contentEditable 和 selection API 是恶魔双星,那就尽可能离它们远点。所以,方案是什么? 世界是多么美好,空气是多么清新。展现在你面前的,都是常用的 API 或者你自己的代码,全部是可控的。和可怕的绝望说拜拜。Selection API 和选区仅仅用于和浏览器进行交互,但在内部你实现了更适用于文本编辑的、不同的机制。再也不需要和那些不确定的 DOM 打交道,只需要操作 DOM 的文本、样式、索引(indexex)和变换(translation)。现在,应用加粗是一个非常简单的算法:给选中的内容添加一个样式,然后自动变换更新 DOM。 冰箱里的食物还不完美,所以你把项目发布到 GitHub 上,开始收到第一个 bug 反馈,不要紧张,没有一个软件是一天炼成的。 Q:我该怎样输入波兰文字? 当我按下 Alt+L,我希望插入的是 “ł”,但你的编辑器插入的是 “l”。 A:对的,一起来看看我们可以做些什么。我们不知道键盘布局,所以不能简单地检查 Alt 的状态。此外,语言的种类也太多了吧喂。好吧,DOM3(DOM level 3)已经有 KeyboardEvent.key 了,但目前仅 IE 和 Firefox 支持,Blink 很快也会支持(译者注:Chrome 51.0 已支持),所以你可以再等等,很有可能所有的主流浏览器会在合适的时间内支持该特性。 Q:我该怎样输入重音符号? 我有一个西班牙语的键盘布局,当我输入一个 “`” ,再输入一个字母(如 “u”),我希望先看到那个标记,然后在同个位置出现字母 “ù”。 A:等等,啥?他是说两次按键会合体成一个字母吗?你他喵的在逗我……我们可以对这种情况做特殊处理(前提是能够获取到当前正在使用的键盘布局),让我们祈祷不会有太多的语言需要这么搞。 Q:我该怎样输入平假名? 在输入时,相关内容可以自动添加下划线,并弹出这种组合弹框。 A:对的,复合事件(composition events)。你知道可以利用这个做点什么(如果浏览器支持的话)。但不幸的是,你会发现 输入法引擎(Input Method Engine)在每个操作系统中表现都不一样,而且通常是系统集成的(例如,它会学习新词汇并实现智能补全)。你再一次感到生无可恋。然后,突然灵光一闪:可以搞一个隐藏的文本区域(hidden textarea),从中读取输入呀,如果把它放到插入符号(caret)的位置,不就可以搞这个弹框了吗,只是失去了上下文建议。打开 IME API 还有些工作要做,你总有一天会放弃这些黑科技的。 译者注: conposition events:复合事件,输入的字符重新组合成文字的过程: IME:Input Method Editors,输入法编辑器。 caret:插入符号,就是编辑的时候那个一闪一闪的竖线。 Q:我该怎样使用 iPad 输入? 当我触碰编辑器时,键盘没有出现。 A:可编辑区域没有获得焦点 = 没有键盘。当然,通过一个隐藏的文本区域(hidden textarea)可以变相解决,真的吗?那你现在怎么粘贴? Q:Alt + 左/右方向键导航时,光标跳过了不止一个单词 “ພາສາຈີນແມ່ນພາສາໜຶ່ງທີ່ເວົ້າໃນປະເທດຈີນ.” 这个文本包含了很多个单词,但你的编辑器把它当成一个单词处理了。 A:问题是,空格在哪?! Q:在 iPhone 上“摇一摇撤销操作” 不起作用 A:对的,必须用 Ctrl + Z,是时候试下加速器(accelerometer)了。 Q:在 iPhone/iPad 上,当我操作一个选取时,键盘不见了 A:对的,焦点从隐藏的文本区域(hidden textarea)移动到文档(document)了…… Q:拼写检查失效了 A:这太可怕了!太可怕了! Q:你的编辑器不能同时使用键盘和屏幕阅读器(Screen Reader) 你的编辑器根本用不了。通常,当我进入编辑器时,屏幕阅读器会通知我,然后,当我导航文本时,它会阅读选中的单词,此外,它也会阅读我输入的内容。但在你的编辑器里,啥都木有发生。顺便说下,你有看过 http://www.w3.org/TR/2dcontext/#best-practices 吗? A:是的,目前没有,但很快会有。 …… 在上面的讨论中可以得出,contentEditable 也许真的很糟糕,但它已经在那儿。我相信所有上面提到的问题,总有一天原生 API 都会支持,但请相信我,那天还没到来。 标准化那些复杂的特性是一个非常艰辛的工作,因为你能看到的永远是冰山一角。虽然我已经和 contentEditable 打交道将近 4 年了(当然,和搞了 13 年的 Frederico Knabben 是完全没得比的 :D),但当看到 W3C 的 public-web-apps 和 public-editing-tf 的邮件列表时,仍感到大开眼界。此外,在浏览器中编辑有很多用例,甚至 XWSISWYG 文本编辑器之间也存在差异,所以,锁定浏览器只针对一个用例是不合理的。 说到标准,contentEditable 必然是其中之一,因为我们需要 contentEditable。也许几年之后,我们将有可能无需 contentEditable 就可以实现一个功能齐全、稳定可用的 XYSIWYG 编辑器。所以,W3C 编辑特攻队 在去年成立(我和 Frederico Knabben 都加入了),就“目前的情况该如何处理”展开了激烈的讨论。关于这方面,我将单独写个帖子,因为它看起来很有前景。 (编辑:你可以在 “Fixing ContentEditable” 一文中查看更多关于 contentEditable 标准化的信息。) contentEditable 就像 JavaScript,除了道格拉斯(Douglas Crockford)还没有给它写一本书。它也有好的一面(是,我知道,到底好的多还是坏的多存在争议)。只需要给 HTML 元素添加一个属性,就能启用文本输入、选中内容、键盘导航、拼写检查、拖放、粘贴、撤销管理,太神奇了!这些都是系统集成的,使得编辑器可以使用屏幕阅读器(screen reader)或者在触控设备上运行,并且非常的 国际化。让我们聚焦在这些好的部分,暂时忘掉它不好的一面。 在过去的几年中,CKEditor 的工作让我逐渐意识到,我们正在逐渐使用自己的实现来替代原生的特性,源于一个回车操作的自定义行为、命令(像 execCommand() API,能够应用、移除、检查一个特定的样式(如加粗)的状态)、撤销管理和劫持粘贴操作以过滤粘贴内容。然后添加了一些选区系统的实现(比如在模态状态下锁定选中内容),加强在表格和列表编辑中的导航功能。从 4.0 版本开始,CKEditor 拥有了自己的 “插入 HTML 到选区” 机制和一个 允许到达不可编辑位置 的特性。CKEditor 4.1 引入了高定制化的 内容过滤器(粘贴操作不再乱七八糟)。CKEditor 4.3 带来对 不可编辑区块包含可编辑区块(non-editable islands with editable islands inside)的支持,这个需要重写许多原生的行为(selection、keyboard、focus、clipboard)。与此同时,我们实现了 Blink 和 WebKit 中对自定义退格和删除的支持。最后,就在几周前,我们完全接管了剪贴板,这意味着在一些浏览器中,复制、剪切和粘贴操作,完全在 CKEditor 的掌握之中。 也就是说,如今的 CKEditor 不仅不让浏览器对内容进行任何处理(除了输入、删除等基本操作),连选区系统、键盘导航和剪贴板、焦点管理等其它相关的 API 也一起接管了。 CKEditor 5 我们计划在编辑器的控制下,仅通过浏览器插入文本就可以完成这个过程(参考:CKEditor 5: The Future of Rich Text Editing)。讽刺的是,我们准备将所有的编辑算法都建立在一个自定义的数据模型之上,我们承认 DOM 不是完成这项任务的最佳工具。当然,我们也到了面临国际化问题的时候了(如对 Alt+退格的支持),我们都知道这一点。但是,这个功能很可能包含在不久后浏览器开放的第一批特性中,感谢编辑特攻队的辛苦付出。 这一切听起来都很美好,但要实现这个目标仍有大量的工作需要完成。即使知道这似乎不是一个独立开发者、甚至是中等规模公司可以完成的项目,但我们相信,终将会出现一个编辑框架,允许其他开发者在其基础上建立自己的定制化方案,这就是我们给 CKEditor 5 定下的目标,虽然 Alloy Editor 这样的项目已经证明即使基于 CKEditor 4 也是可行的。如果不是开发者一旦发现现有的编辑器无法满足需求,就立刻从一个光秃秃的 contentEditable 开始倒腾,我们今天可能在一个不同的地方 ;)。
或
手机扫一扫
移动阅读更方便
你可能感兴趣的文章