[译] 通过 contentEditable 属性创建一个所见即所得的编辑器(富文本编辑器)
译者注
这只是一篇入门教程,介绍了一些基础知识,仅供参考,切不可因此觉得富文本编辑器很简单。
创建富文本编辑器是一个非常复杂的工程,需要考虑到方方面面,也有很多坑(请参考原文第一条评论)。
为免误导大家,特此说明。
格式说明:链接,名词,命令
原文: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)按钮类似。
希望大家喜欢这篇教程,并能从中学到一些新的东西。
[译] 通过 contentEditable 属性创建一个所见即所得的编辑器(富文本编辑器)的更多相关文章
- html5中contenteditable属性如果过滤标签,过滤富文本样式
在div中使用contenteditable=”true”可以达到模拟输入框的效果,但是当我们复制其他网页内容进去的时候,会发现连带的样式也一起复制进去了.很明显我们不需要复制富文本样式,那么如何 ...
- 从零开始, 开发一个 Web Office 套件 (1): 富文本编辑器
这是一个系列博客, 最终目的是要做一个基于HTML Canvas 的, 类似于微软 Office 的 Web Office 套件, 包括: 文档, 表格, 幻灯片... 等等. 富文本编辑器 万里长征 ...
- [前端随笔][JavaScript] 制作一个富文本编辑器
写在前面 现在网上有很多现成的富文本编辑器,比如百度家的UEditor,kindeditor,niceditor等等,功能特别的多,API也很多,要去熟悉他的规则也很麻烦,所以想自己了解一下原理,做一 ...
- ASP.NET MVC + 百度富文本编辑器 + EasyUi + EntityFrameWork 制作一个添加新闻功能
本文将交大伙怎么集成ASP.NET MVC + 百度富文本编辑器 + EasyUi + EntityFrameWork来制作一个新闻系统 先上截图: 添加页面如下: 下面来看代码部分 列表页如下: @ ...
- 基于ABP做一个简单的系统——实战篇:4.基于富文本编辑器,Razor模板引擎生成内容并导出Word 填坑记录
起因 需求是这样的,有一种协议需要生成,协议的模板是可配置的,在生成过程中,模板中的内容可以根据约定的标记进行替换(就像mvc的razor模板一样).生成后的内容还需要导出成word或pdf. 常见的 ...
- 从零开始, 开发一个 Web Office 套件 (2): 富文本编辑器
书接前文: 从零开始, 开发一个 Web Office 套件 (1): 富文本编辑器 这是一个系列博客, 最终目的是要做一个基于HTML Canvas 的, 类似于微软 Office 的 Web Of ...
- linq to sql用partial扩展属性,创建一个部分类(用于多表连接)
1.在窗体中创建dataGridView显示表: using System; using System.Collections.Generic; using System.ComponentModel ...
- 一个简单的Android富文本TextView实现
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 24.0px Helvetica; color: #555555 } p.p2 { margin: 0.0p ...
- 实现一个最简单的VIM文本编辑器(可能有bug,随便写了一个)
简单的写了一个文本编辑器,功能很简单,但足以把文件IO相关的操作熟悉了,可能功能或者分配的大小还不够完善.请参考参考: #include <stdio.h> #include <co ...
随机推荐
- C 实现删除非空文件夹
/* 文件名: rd.c ---------------------------------------------------- c中提供的对文件夹操作的函数,只能对空文件夹进行 删除,这使很多 ...
- 解决:java.lang.ArrayIndexOutOfBoundsException: 160 at com.alibaba.fastjson.serializer.SerializeWriter.writeStringWithDoubleQuote(SerializeWriter.java:868)
今天线上遇到一个问题,从hbase里读取出来的数据在转换json后输出时出现异常: java.lang.ArrayIndexOutOfBoundsException: 160 at com.aliba ...
- 本地yum仓库的搭建
. 1.直接断开网络,模拟生产内网环境 2.将原先的网络yum仓库全部移动到 backup目录下 3.创建本地yum仓库 local_yum.repo vi /etc/yum.repos.d/loc ...
- 恢复安装过树莓派相关操作系统的TF卡容量
原文地址:传送门 前言玩树莓派的都知道,当我们向TF卡写入系统后,在Windows下能识别的只有几百M的容量了,这主要是由于在装Linux系统的时候给TF卡分了Windows无法识别的分区,下面我用图 ...
- HDU 6237.A Simple Stone Game-欧拉函数找素因子 (2017中国大学生程序设计竞赛-哈尔滨站-重现赛)
A Simple Stone Game Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Ot ...
- 代码编辑器[0] -> Vim/gVim[2] -> Vim 的相关知识
相关知识 / Relevant Knowledge 1 _vimrc编程 / _vimrc Program 1. 注释符", 用于注释 2. 关键词set, 用于设置功能等 3. 关键词im ...
- 每天一个Linux命令(10)cp命令
cp命令用来将一个或多个源文件或者目录复制到指定的目的文件或目录.它可以将单个源文件复制成一个指定文件名的具体的文件或一个已经存在的目录下.cp命令还支持同时复制多个文件,当一次复制多个文件时,目标文 ...
- hdu 1556 Color the ball 线段树
题目链接:HDU - 1556 N个气球排成一排,从左到右依次编号为1,2,3....N.每次给定2个整数a b(a <= b),lele便为骑上他的“小飞鸽"牌电动车从气球a开始到气 ...
- service mysqld start 报错:service mysqld start 报错 090517 13:34:15 [ERROR] Can't open the mysql.plugin table. Please run mysql_upgrade to create it. 090Can't open the mysql.plugin table. Please run mysql
service mysqld start 报错 090517 13:34:15 [ERROR] Can't open the mysql.plugin table. Please run mysql_ ...
- codeigniter与swfupload完整解决方案
转自:http://blog.sina.com.cn/s/blog_6d8dc8eb0100s4bv.html codeigniter(简称ci)有研究了一段时间了,看重的是ci的轻量,便捷,最近公司 ...