译者注

这只是一篇入门教程,介绍了一些基础知识,仅供参考,切不可因此觉得富文本编辑器很简单。

创建富文本编辑器是一个非常复杂的工程,需要考虑到方方面面,也有很多坑(请参考原文第一条评论)。

为免误导大家,特此说明。

格式说明:链接,名词,命令


原文: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 方法可以进行各种编辑操作,如 添加链接、选中文本 加粗 或 斜体,修改 字体 或 字体颜色,使用语法如下:

  1. document.execCommand(CommandName, ShowDefaultUI, ValueArgument);

CommandName:DOMString,指定所要执行的命令。

ShowDefaultUI:Boolean,指定是否显示用户界面,该选项还未完全实现,一般设置为 false。

ValueArgument:提供命令的附加参数,如图片URL或颜色值。不需要附加参数时,设置为 null。

我们将使用不同的命令来实现各种功能,下面逐一介绍。

无附加参数的命令

部分命令不需要附加参数(ValueArgument),如加粗(bold)、对齐(justify)、撤销(undo)、重做(redo),使用下面的语法:

  1. document.execCommand(commandName, false, null);

CommandName 为命令名,如 justifyCenterjustifyRightbold 等。

带附加参数的命令

部分命令需要传入相应的附加参数,如插入图片(insertImage)、创建链接(createLink)、字体颜色(foreColor),使用下面的语法:

  1. document.execCommand(commandName, false, value);

commandName 为 insertImage 时,value 为将要插入的图片的 URL。

commandName 为 foreColor 时,value 为颜色值字符串,如 #FF9966、blue。

添加块样式标签的命令

添加 HTML 块样式标签(Block-Style Tags),需要将 commandName 指定为 formatBlock、ValueArgument 设置为标签名(tag name),使用下面的语法:

  1. document.execCommand('formatBlock', false, tagName);

该命令将为当前选中行添加一个 HTML 块样式标签,如果本身已带有标签,则将被替换掉。

tagName 为块样式标签名,如标题标签(h1-h6)、段落标签(p)或块引用(blockquote)。

上面是一些最常用的命令,更多信息请参考 document.execCommand API 文档,那里有所有可用命令的列表。

创建一个工具栏

了解了基础知识,下面我们来创建一个工具栏,工具栏的按钮我使用了 Font Awesome 图标。

大家可能也注意到了,除了少数区别,所有的 execCommand 命令都有类似的结构,基于这个特点,我们可以使用下面的节点结构来定义工具栏的按钮:

  1. <a href="#" data-command='commandName'><i class='fa fa-icon'></i></a>

通过这种方式,当用户点击按钮时,我们可以从 data-command 属性中获取到 execCommand 所要执行的命令。

例如下面这些例子:

  1. <a href="#" data-command='h2'>H2</a>
  2. <a href="#" data-command='undo'><i class='fa fa-undo'></i></a>
  3. <a href="#" data-command='createlink'><i class='fa fa-link'></i></a>
  4. <a href="#" data-command='justifyLeft'><i class='fa fa-align-left'></i></a>
  5. <a href="#" data-command='superscript'><i class='fa fa-superscript'></i></a>

第一个按钮的 data-command 属性值为 h2,在 JavaScript 中获取到这个值后,我们将使用 execCommand 方法的 添加块样式标签的命令。同样的,最后一个按钮,superscript 则表示应该使用 无附加参数的命令

创建字体颜色(foreColor)和背景颜色(backColor)按钮则是另外一种实现方法,这里主要有两个问题。

第一个问题是,提供给用户选择的颜色越多,需要编写的代码就越多,很麻烦而且容易出错。可以使用下面的 JavaScript 来处理这个问题:

  1. var colorPalette = ['000000', 'FF9966', '6699FF', '99FF66','CC0000', '00CC00', '0000CC', '333333', '0066FF', 'FFFFFF'];
  2.  
  3. var forePalette = $('.fore-palette');
  4.  
  5. for (var i = 0; i < colorPalette.length; i++) {
  6. forePalette.append('<a href="#" data-command="forecolor" data-value="' + '#' + colorPalette[i] + '" style="background-color:' + '#' + colorPalette[i] + ';" class="palette-item"></a>');
  7. }

注意这里我也给每个颜色值设置了一个 data-value 属性,后面可以作为 execCommand 方法的 ValueArgument 参数。

第二个问题是,我们总不能一直显示那么多颜色块吧,这样会占用很大的空间,给用户带来不好的体验。通过一些简单的 CSS,就可以实现一个不错的效果:只有当用户把鼠标移上按钮时,才会显示调色板。

按钮的节点结构需要调整为:

  1. <div class="fore-wrapper"><i class='fa fa-font'></i>
  2. <div class="fore-palette">
  3. </div>
  4. </div>

要让按钮在鼠标移上时才显示,我们需要添加以下 CSS:

  1. .fore-palette,
  2. .back-palette {
  3. display: none;
  4. }
  5.  
  6. .fore-wrapper:hover .fore-palette,
  7. .back-wrapper:hover .back-palette {
  8. display: block;
  9. float: left;
  10. position: absolute;
  11. }

在 CodePen 的 示例 中,还有许多其它的 CSS 代码用于美化工具栏,但核心功能所需的就只有上面这些。

给编辑器添加功能

现在,是时候来实现我们的编辑器的功能了。所需的代码惊人的少:

  1. $('.toolbar a').click(function(e) {
  2.  
  3. var command = $(this).data('command');
  4.  
  5. if (command == 'h1' || command == 'h2' || command == 'p') {
  6. document.execCommand('formatBlock', false, command);
  7. }
  8.  
  9. if (command == 'forecolor' || command == 'backcolor') {
  10. document.execCommand($(this).data('command'), false, $(this).data('value'));
  11. }
  12.  
  13. if (command == 'createlink' || command == 'insertimage') {
  14. url = prompt('Enter the link here: ','http:\/\/');
  15. document.execCommand($(this).data('command'), false, url);
  16. }
  17.  
  18. else document.execCommand($(this).data('command'), false, null);
  19.  
  20. });

我们侦听了工具栏上所有按钮的点击事件,当按钮被点击时,将其 data-commond 属性的值保存到变量 commond 中,用于后面执行 execCommand 方法的相应命令,避免重复获取,也使代码更加简洁。

设置字体颜色(foreColor)和背景颜色(backColor)时,使用了 data-value 属性的值作为第三个参数。

对于创建链接(createLink)和插入图片(insertImage)命令所需的 url 参数,使用了一个提示弹框(prompt)来获取用户输入的值。当然你也可以添加一些额外的逻辑来检测 url 的有效性。

如果 command 变量不满足上面所有的 if 条件,则执行默认的 execCommand 命令。

这个就是我们实现出来的 WYSIWYG 编辑器

你也可以使用我 上个教程 介绍的 localStorage 来实现自动保存功能。

跨浏览器差异

对于 execCommand,不同的浏览器会有不同的实现。例如 formatBlock,IE 只支持标题标签(h1-h6)、addresspre,甚至在指定 commandName 时还需要包含标签符,如 <h3>

也不是所有的浏览器都支持所有的命令,IE 就不支持 insertHTMLhiliteColorinsertBrOnReturn 只有 Firefox 支持。

更多浏览器差异,可以参考 这个 GitHub 页面

最后的感想

创建自己的 WYSIWYG 编辑器是一个非常好的学习过程,在本篇教程中,讨论了许多 execCommand 命令,使用了一些 CSS 来处理基础外观。作为练习,建议大家通过文本选择器(text selection)实现一个工具栏按钮,用于设置字体(font),实现方法和字体颜色(foreColor)按钮类似。

希望大家喜欢这篇教程,并能从中学到一些新的东西。

[译] 通过 contentEditable 属性创建一个所见即所得的编辑器(富文本编辑器)的更多相关文章

  1. html5中contenteditable属性如果过滤标签,过滤富文本样式

    ​​在div中使用contenteditable=”true”可以达到模拟输入框的效果,但是当我们复制其他网页内容进去的时候,会发现连带的样式也一起复制进去了.很明显我们不需要复制富文本样式,那么如何 ...

  2. 从零开始, 开发一个 Web Office 套件 (1): 富文本编辑器

    这是一个系列博客, 最终目的是要做一个基于HTML Canvas 的, 类似于微软 Office 的 Web Office 套件, 包括: 文档, 表格, 幻灯片... 等等. 富文本编辑器 万里长征 ...

  3. [前端随笔][JavaScript] 制作一个富文本编辑器

    写在前面 现在网上有很多现成的富文本编辑器,比如百度家的UEditor,kindeditor,niceditor等等,功能特别的多,API也很多,要去熟悉他的规则也很麻烦,所以想自己了解一下原理,做一 ...

  4. ASP.NET MVC + 百度富文本编辑器 + EasyUi + EntityFrameWork 制作一个添加新闻功能

    本文将交大伙怎么集成ASP.NET MVC + 百度富文本编辑器 + EasyUi + EntityFrameWork来制作一个新闻系统 先上截图: 添加页面如下: 下面来看代码部分 列表页如下: @ ...

  5. 基于ABP做一个简单的系统——实战篇:4.基于富文本编辑器,Razor模板引擎生成内容并导出Word 填坑记录

    起因 需求是这样的,有一种协议需要生成,协议的模板是可配置的,在生成过程中,模板中的内容可以根据约定的标记进行替换(就像mvc的razor模板一样).生成后的内容还需要导出成word或pdf. 常见的 ...

  6. 从零开始, 开发一个 Web Office 套件 (2): 富文本编辑器

    书接前文: 从零开始, 开发一个 Web Office 套件 (1): 富文本编辑器 这是一个系列博客, 最终目的是要做一个基于HTML Canvas 的, 类似于微软 Office 的 Web Of ...

  7. linq to sql用partial扩展属性,创建一个部分类(用于多表连接)

    1.在窗体中创建dataGridView显示表: using System; using System.Collections.Generic; using System.ComponentModel ...

  8. 一个简单的Android富文本TextView实现

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 24.0px Helvetica; color: #555555 } p.p2 { margin: 0.0p ...

  9. 实现一个最简单的VIM文本编辑器(可能有bug,随便写了一个)

    简单的写了一个文本编辑器,功能很简单,但足以把文件IO相关的操作熟悉了,可能功能或者分配的大小还不够完善.请参考参考: #include <stdio.h> #include <co ...

随机推荐

  1. django使用mysql的设置与迁移

    1.创建数据库 create database django_lianxi charset=utf8; 2.django项目文件夹的setting.py设置 Django项目默认 sqlite3 数据 ...

  2. POJ 3620 Avoid The Lakes【DFS找联通块】

    Avoid The Lakes Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 6826   Accepted: 3637 D ...

  3. Codeforces 825E - Minimal Labels

    825E - Minimal Labels 题意 给出 m 条有向边,组成有向无环图,输出一个 1 到 n 组成的排列,每个数只能出现一次,表示每个点的标号.如果有边 \((u, v)\) 那么 \( ...

  4. luogu P1122 最大子树和

    题目描述 小明对数学饱有兴趣,并且是个勤奋好学的学生,总是在课后留在教室向老师请教一些问题.一天他早晨骑车去上课,路上见到一个老伯正在修剪花花草草,顿时想到了一个有关修剪花卉的问题.于是当日课后,小明 ...

  5. 队列 LinkedBlockingQueue

    1 api     java.util.concurrent包下的新类.LinkedBlockingQueue就是其中之一,是一个阻塞的线程安全的队列,底层采用链表实现.             Li ...

  6. UIActivityIndicatorView活动指示器

    活动指示器(UIActivityIndicatorView)可以告知用户有一个操作正在进行中.派生自UIView,所以他是视图,也可以附着在视图上. 一.创建 UIActivityIndicatorV ...

  7. 彻底理解Java中的hashcode方法(转)

    本文转自http://www.importnew.com/18851.html 哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率.在Java的Object类中有 ...

  8. redis--AOF

    Redis 分别提供了 RDB 和 AOF 两种持久化机制: RDB 将数据库的快照( snapshot)以二进制的方式保存到磁盘中. 相当于MySQL binlog 的 raw模式 AOF 则以协议 ...

  9. App Distribution Guide (二)

    Configuring Your Xcode Project for Distribution  You can edit your project settings anytime, but som ...

  10. background属性总结,background-image路径问题相对于css和js

    (1)background属性总结 background 简写属性在一个声明中设置所有的背景属性. 可以设置如下属性:background-color规定要使用的背景颜色.background-pos ...