译者注
这只是一篇入门教程,介绍了一些基础知识,仅供参考,切不可因此觉得富文本编辑器很简单。
创建富文本编辑器是一个非常复杂的工程,需要考虑到方方面面,也有很多坑(请参考原文第一条评论)。
为免误导大家,特此说明。
格式说明:链接,名词,命令
原文:Create a WYSIWYG Editor With the contentEditable Attribute
WYSIWYG 编辑器非常常用,你或许在某个时候也使用过。
WYSIWYG:What You See Is What You Get,所见即所得。
通过一些第三方库,你可以快速地创建自己的编辑器,但也有一些明显的缺陷。首先,它们很重,许多特性你压根就用不到。另外,定制外观也非常蛋疼。
本文中,我们将按自己喜欢的风格,创建一个拥有基本格式化功能的、轻量的所见即所得编辑器。
我们将从介绍 execCommand 开始,因为在实现编辑器的过程中,我们会大量使用到该方法。
Document.execCommand()
execCommand 是 document 对象的一个方法,它提供了操作可编辑区域的内容的能力,和 contentEditable 配合使用,就可以实现一个富文本编辑器(rich-text editor)。
execCommand 方法可以进行各种编辑操作,如 添加链接、选中文本 加粗 或 斜体,修改 字体 或 字体颜色,使用语法如下:
document.execCommand(CommandName, ShowDefaultUI, ValueArgument);
CommandName:DOMString,指定所要执行的命令。
ShowDefaultUI:Boolean,指定是否显示用户界面,该选项还未完全实现,一般设置为 false。
ValueArgument:提供命令的附加参数,如图片URL或颜色值。不需要附加参数时,设置为 null。
我们将使用不同的命令来实现各种功能,下面逐一介绍。
无附加参数的命令
部分命令不需要附加参数(ValueArgument),如加粗(bold)、对齐(justify)、撤销(undo)、重做(redo),使用下面的语法:
document.execCommand(commandName, false, null);
CommandName 为命令名,如 justifyCenter、justifyRight、bold 等。
带附加参数的命令
部分命令需要传入相应的附加参数,如插入图片(insertImage)、创建链接(createLink)、字体颜色(foreColor),使用下面的语法:
document.execCommand(commandName, false, value);
commandName 为 insertImage 时,value 为将要插入的图片的 URL。
commandName 为 foreColor 时,value 为颜色值字符串,如 #FF9966、blue。
添加块样式标签的命令
添加 HTML 块样式标签(Block-Style Tags),需要将 commandName 指定为 formatBlock、ValueArgument 设置为标签名(tag name),使用下面的语法:
document.execCommand('formatBlock', false, tagName);
该命令将为当前选中行添加一个 HTML 块样式标签,如果本身已带有标签,则将被替换掉。
tagName 为块样式标签名,如标题标签(h1-h6)、段落标签(p)或块引用(blockquote)。
上面是一些最常用的命令,更多信息请参考 document.execCommand API 文档,那里有所有可用命令的列表。
创建一个工具栏
了解了基础知识,下面我们来创建一个工具栏,工具栏的按钮我使用了 Font Awesome 图标。
大家可能也注意到了,除了少数区别,所有的 execCommand 命令都有类似的结构,基于这个特点,我们可以使用下面的节点结构来定义工具栏的按钮:
<a href="#" data-command='commandName'><i class='fa fa-icon'></i></a>
通过这种方式,当用户点击按钮时,我们可以从 data-command 属性中获取到 execCommand 所要执行的命令。
例如下面这些例子:
<a href="#" data-command='h2'>H2</a> <a href="#" data-command='undo'><i class='fa fa-undo'></i></a> <a href="#" data-command='createlink'><i class='fa fa-link'></i></a> <a href="#" data-command='justifyLeft'><i class='fa fa-align-left'></i></a> <a href="#" data-command='superscript'><i class='fa fa-superscript'></i></a>
第一个按钮的 data-command 属性值为 h2,在 JavaScript 中获取到这个值后,我们将使用 execCommand 方法的 添加块样式标签的命令。同样的,最后一个按钮,superscript 则表示应该使用 无附加参数的命令。
创建字体颜色(foreColor)和背景颜色(backColor)按钮则是另外一种实现方法,这里主要有两个问题。
第一个问题是,提供给用户选择的颜色越多,需要编写的代码就越多,很麻烦而且容易出错。可以使用下面的 JavaScript 来处理这个问题:
var colorPalette = ['000000', 'FF9966', '6699FF', '99FF66','CC0000', '00CC00', '0000CC', '333333', '0066FF', 'FFFFFF']; var forePalette = $('.fore-palette'); for (var i = 0; i < colorPalette.length; i++) { forePalette.append('<a href="#" data-command="forecolor" data-value="' + '#' + colorPalette[i] + '" style="background-color:' + '#' + colorPalette[i] + ';" class="palette-item"></a>'); }
注意这里我也给每个颜色值设置了一个 data-value 属性,后面可以作为 execCommand 方法的 ValueArgument 参数。
第二个问题是,我们总不能一直显示那么多颜色块吧,这样会占用很大的空间,给用户带来不好的体验。通过一些简单的 CSS,就可以实现一个不错的效果:只有当用户把鼠标移上按钮时,才会显示调色板。
按钮的节点结构需要调整为:
<div class="fore-wrapper"><i class='fa fa-font'></i> <div class="fore-palette"> </div> </div>
要让按钮在鼠标移上时才显示,我们需要添加以下 CSS:
.fore-palette, .back-palette { display: none; } .fore-wrapper:hover .fore-palette, .back-wrapper:hover .back-palette { display: block; float: left; position: absolute; }
在 CodePen 的 示例 中,还有许多其它的 CSS 代码用于美化工具栏,但核心功能所需的就只有上面这些。
给编辑器添加功能
现在,是时候来实现我们的编辑器的功能了。所需的代码惊人的少:
$('.toolbar a').click(function(e) { var command = $(this).data('command'); if (command == 'h1' || command == 'h2' || command == 'p') { document.execCommand('formatBlock', false, command); } if (command == 'forecolor' || command == 'backcolor') { document.execCommand($(this).data('command'), false, $(this).data('value')); } if (command == 'createlink' || command == 'insertimage') { url = prompt('Enter the link here: ','http:\/\/'); document.execCommand($(this).data('command'), false, url); } else document.execCommand($(this).data('command'), false, null); });
我们侦听了工具栏上所有按钮的点击事件,当按钮被点击时,将其 data-commond 属性的值保存到变量 commond 中,用于后面执行 execCommand 方法的相应命令,避免重复获取,也使代码更加简洁。
设置字体颜色(foreColor)和背景颜色(backColor)时,使用了 data-value 属性的值作为第三个参数。
对于创建链接(createLink)和插入图片(insertImage)命令所需的 url 参数,使用了一个提示弹框(prompt)来获取用户输入的值。当然你也可以添加一些额外的逻辑来检测 url 的有效性。
如果 command 变量不满足上面所有的 if 条件,则执行默认的 execCommand 命令。
这个就是我们实现出来的 WYSIWYG 编辑器:
你也可以使用我 上个教程 介绍的 localStorage 来实现自动保存功能。
跨浏览器差异
对于 execCommand,不同的浏览器会有不同的实现。例如 formatBlock,IE 只支持标题标签(h1-h6)、address 和 pre,甚至在指定 commandName 时还需要包含标签符,如 <h3>。
也不是所有的浏览器都支持所有的命令,IE 就不支持 insertHTML 和 hiliteColor,insertBrOnReturn 只有 Firefox 支持。
更多浏览器差异,可以参考 这个 GitHub 页面。
最后的感想
创建自己的 WYSIWYG 编辑器是一个非常好的学习过程,在本篇教程中,讨论了许多 execCommand 命令,使用了一些 CSS 来处理基础外观。作为练习,建议大家通过文本选择器(text selection)实现一个工具栏按钮,用于设置字体(font),实现方法和字体颜色(foreColor)按钮类似。
希望大家喜欢这篇教程,并能从中学到一些新的东西。