如果你还不知道什么是富文本编辑器(Rich Text Editor),那么我强烈建议你先去百度了解一下。富文本编辑器(以下简称RTE)是一种所见即所得的网页编辑器。最常见的比如论坛发帖编辑发布框、写作类网站的编辑器。此文并不是详细描述如何用js去开发一个RTE,主要目的是介绍制作富文本编辑器的思路以及方法,还有就是在开发过程中遇到的问题。若你想详细知道如何去开发一个RTE,可在阅读完本文之后查看demo源代码。

我们知道,可以让网页处于编辑状态可以通过以下三种形式:

第一种:文本域。

文本域是网页中最常见的输入框,不过它只是文本框,只能输入文字,不可输入图片或者链接等多媒体元素,因此它必然不是我们想要的。

第二种:添加 contenteditable属性为true的div

contenteditable这个属性连低版本的ie都支持,QQ空间的心情发布框就是带有此属性的div,因此我们可以想到用类似的方法做RTE,但是本文介绍的这个RTE并不是利用div制作的,为什么?文章结尾处会提及。

第三种:设置了designMode为”on”的iframe

在网页中插入iframe,那么iframe就可以看做是一个单独的文档,我们再把iframe的designMode属性设置为on,结合js提供的execCommand方法。即可在这个文档中实现编辑、插入等设置功能,这也是本篇所要说的。

制作RTE

Js提供了操作文档的方法,即document.execCommand。此方法有三个参数,用法如下:

document.execCommand(commandName,false,[null|string]);

第一个参数commandName:要执行的命令名称

第二个参数false:应该始终设置为false,因为在firefox中若参数为true时报错

第三个参数null或者字符串:表示浏览器是否应该为当前命令提供用户界面的一个布尔值和执行命令必须的一个值(如果不需要值,则传递null)

下表列出了开发中所用到的参数:

命令 (第一个参数) 值(第三个参数) 说明
bold null 将选择的文字加粗
italic null 将选择的文字转换为斜体
underline null 将选择的文字加下划线
strikethrough null 将选择的文字加删除线
fontsize 1-7 将选择的文字修改为指定字体大小
justifyleft null 将选择的文字居左
justifycenter null 将选择的文字居中
justifyright null 将选择的文字居右
indent null 左缩进
outdent null 右缩进
insertorderedlist null 插入有序列表
insertunorderedlist null 插入无序列表
undo null 撤销
redo null 重复
removeformat null 将选择的文字清除格式
forecolor 颜色字符串 设置文档背景色
backcolor 颜色字符串 将选择的文字修改为指定颜色

倘若我们需要对iframe里的内容做特殊操作,比如预览编辑好的内容、或者切换到源码等,因为document.execCommand没有相关参数,那么document.execCommand就用不上了。此时就需要添加一些自定义方法,下表也列出了这些功能以及相关做法:

命令 说明 功能实现描述
insertimage 插入图片 通过iframe的innerHTML拼接
createlink 插入链接 通过prompt输入,插入到文档
inserttime 插入时间 获取时间对象,插入到文档
insertface 插入表情 通过iframe的innerHTML拼接
insertsymbol 插入特殊符号 通过iframe的innerHTML拼接
inserttable 插入表格 生成table结构,通过iframe的innerHTML拼接
save 保存当前内容 execCommand中的savesa命令,其他浏览器给出提示
new 新建编辑内容 confirm用户,确认后清空iframe的innerHTML
preview 预览当前内容 将iframe内容复制给弹层,显示弹层
html 切换视图与源码 textarea获取iframe的html源码,控制显示隐藏

我们对相应标签设置一个data-command或self-command属性,属性值为以上两个表格列出的命令。然后当用户点击这些标签时,获取data-command(self-command)的属性值,执行相应的操作即可。

浏览器兼容性的处理

1.低版本ie不支持新建窗口预览。

通常我们在做代码效果预览时,都是采用新建空白窗口,然后将文本域内容写入到新窗口中,即:

window.open().document.write(iframeDocument.body.innerHTML);

但是在低版本ie不支持此方法,于是采用当前页面弹框形式。

2.低版本的ie中,点击iframe以外的元素,iframe会失去焦点,回到iframe第一行最开始编辑的位置。

解决方案是给选择功能的元素加unselectable属性,并设置为on。

ele[i].setAttribute("unselectable" , "on");

3.低版本ie在execCommand方法中不支持三位数的颜色。

前面提到,iframe文档中有这么一个方法:

iframeDocument.execCommand(commandType,false,value);

其中的val可以是字体的大小值,也可以为颜色值等。在处理iframe中的颜色(文字颜色、文字背景色)时,我一开始直接用我前面写的colorTake.js提取颜色,因为这个js文件获取的颜色值默认为三位,后来发现三位数值的颜色在低版本ie中根本无法生效,于是修改colorTake.js返回六位的数值。

4.将iframe保存为页面。

在开发过程中发现,只有低版本的ie才支持execCommand的saveas命令,所以需要对其他浏览器做提示处理。

if(!+[1,]){
iframeDocument.execCommand("saveas",false,"document.html");
}
else{
alert("对不起,当前浏览器出于安全机制禁止保存,你可以尝试手动保存或使用IE浏览器!");
}

为什么不用div代替iframe

  1. 当前文档的行为不会影响iframe中的元素。也就是说,当前页面定义的样式和js操作,对iframe中相同类名的元素是无效的。
  2. 对于文档操作,js提供了一套现成的execCommand方法。倘若我们用div来开发RTE,在没有现成方法的基础,那么就需要先提取选中的文字,可以参见我之前写的文字选中分享到”简单实现,然后再对选中的文字对象做处理,处理完后再获取光标位置,最后插入到对应的位置,这当中所涉及到的方法兼容性都一塌糊涂,而且方法名都很长,谁搞谁知道。

文章末尾,来用下这款 富文本编辑器 吧。