深入理解javascript中的富文本编辑
前面的话
一说起富文本,人们第一印象就是像使用word一样,在网页上操作文档。实际上差不多就是这样。富文本编辑,又称为WYSIWYG (What You See Is What You Get所见即所得),指在网页中编辑富文本内容。本文将详细介绍如何通过javascript实现富文本编辑
方式
有两种编辑富文本的方式,一种是使用iframe元素,另一种是使用contenteditable属性
【1】iframe
在页面中嵌入一个包含空HTML页面的iframe。通过设置designMode属性,这个空白的HTML页面可以被编辑,而编辑对象则是该页面<body>元素的HTML代码
designMode属性有两个可能的值:"off"(默认值)和"on"。在设置为"on"时,整个文档都会变得可以编辑
只有在页面完全加载之后才能设置designMode属性。因此,在包含页面中,需要使用onload事件处理程序
[注意]此方法必须在服务器端才能执行,否则会提示跨域安全提示
<iframe name="wysiwyg" src="wysiwyg.html" style="height: 100px;width: 100px;"></iframe>
<script>
window.onload= function(){
frames['wysiwyg'].document.designMode = 'on';
}
</script>
【2】contenteditable
把contenteditable属性应用给页面中的任何元素,然后用户立即就可以编辑该元素
设置document.designMode='on'时,页面的任意位置都可以编辑;使用contenteditable='true'则只对具体元素和其包含的元素起作用
[注意]一定要区分contenteditable和contentEditable。contenteditable是元素的特性,而contentEditable是对象的属性
<div id="wysiwyg" style="height: 100px;width: 100px;border:1px solid black"></div>
<button id="btn1">打开富文本编辑</button>
<button id="btn2">关闭富文本编辑</button>
<script>
btn1.onclick = function(){wysiwyg.contentEditable = true;}
btn2.onclick = function(){wysiwyg.contentEditable = false;}
</script>
命令
与富文本编辑器交互的主要方式,就是使用document.execCommand()。这个方法可以对文档执行预定义的命令,而且可以应用大多数格式
document.execCommand(String aCommandName, Boolean aShowDefaultUI, String aValueArgument)方法需要传递3个参数
aCommandName表示要执行的命令名称,不可省略
aShowDefaultUI表示是否展示用户界面,默认为false,可省略
aValueArgument表示额外参数值,默认为null,可省略
[注意]为了确保浏览器兼容性,第二个参数应始终设置为false,因为firefox在该参数为true时抛出错误
段落格式
居中 document.execCommand('justifyCenter');
左对齐 document.execCommand('justifyLeft');
右对齐 document.execCommand('justifyRight');
添加缩进 document.execCommand('indent');
去掉缩进 document.execCommand('outdent');
<div id="wysiwyg" style="height: 100px;width: 300px;border:1px solid black" contenteditable>测试内容</div>
<button data-name="justifyCenter">居中</button>
<button data-name="justifyLeft">左对齐</button>
<button data-name="justifyRight">右对齐</button>
<button data-name="indent">添加缩进</button>
<button data-name="outdent">去掉缩进</button>
<script>
var btns = document.getElementsByTagName('button');
for(var i = 0; i < btns.length; i++){
btns[i].onclick = function(){
document.execCommand(this.getAttribute('data-name'));
}
}
</script>
文本格式
字体类型 document.execCommand('fontname',false,sFontName)
字体大小 document.execCommand('fontsize',false,sFontSize)
字体颜色 document.execCommand('forecolor',false,sFontColor)
背景色 document.execCommand('backColor',false,sBackColor)
加粗 document.execCommand('bold');
斜体 document.execCommand('italic');
下划线 document.execCommand('underline');
<div id="wysiwyg" style="height: 100px;width: 300px;border:1px solid black" contenteditable>测试内容</div>
<button data-name="fontname" data-value="宋体">宋体</button>
<button data-name="fontsize" data-value="5">大字体</button>
<button data-name="forecolor" data-value="red">红色字体</button>
<button data-name="backColor" data-value="lightgreen">浅绿背景</button>
<button data-name="bold">加粗</button>
<button data-name="italic">斜体</button>
<button data-name="underline">下划线</button>
<script>
var btns = document.getElementsByTagName('button');
for(var i = 0; i < btns.length; i++){
btns[i].onclick = function(){
document.execCommand(this.getAttribute('data-name'),false,this.getAttribute('data-value'));
}
}
</script>
编辑
复制 document.execCommand('copy');
剪切 document.execCommand('cut');
粘贴 document.execCommand('paste');(经测试无效)
全选 document.execCommand('selectAll');
删除 document.execCommand('delete');
后删除 document.execCommand('forwarddelete');
清空格式 document.execCommand('removeFormat');
前进一步 document.execCommand('redo');
后退一步 document.execCommand('undo');
打印 document.execCommand('print');(对firefox无效)
<div id="wysiwyg" style="height: 100px;width: 300px;border:1px solid black" contenteditable>测试内容</div>
<button data-name="copy">复制</button>
<button data-name="cut">剪切</button>
<button data-name="paste">粘贴</button>
<button data-name="selectAll">全选</button>
<button data-name="delete">删除</button>
<button data-name="forwarddelete">后删除</button>
<button data-name="removeFormat">清空格式</button>
<button data-name="redo">前进一步</button>
<button data-name="undo">后退一步</button>
<button data-name="print">打印</button>
<script>
var btns = document.getElementsByTagName('button');
for(var i = 0; i < btns.length; i++){
btns[i].onclick = function(){
document.execCommand(this.getAttribute('data-name'));
}
}
</script>
插入
插入标签 document.execCommand('formatblock',false,elementName);
插入<hr> document.execCommand('inserthorizontalrule');
插入<ol> document.execCommand('insertorderedlist');
插入<ul> document.execCommand('insertunorderedlist');
插入<p> document.execCommand('insertparagraph');
插入图像 document.execCommand('insertimage',false,URL);
增加链接 document.execCommand('createlink',false,URL);
删除链接 document.execCommand('unlink');
<div id="wysiwyg" style="height: 100px;width: 300px;border:1px solid black;overflow:auto" contenteditable>测试内容</div>
<button data-name="formatblock" data-value="div">插入div</button>
<button data-name="inserthorizontalrule">插入hr</button>
<button data-name="insertorderedlist">插入ol</button>
<button data-name="insertunorderedlist">插入ul</button>
<button data-name="insertparagraph">插入p</button>
<button data-name="insertimage" data-value="http://files.cnblogs.com/files/xiaohuochai/zan.gif">插入图像</button>
<button data-name="createlink" data-value="www.cnblogs.com/xiaohuochai">增加链接</button>
<button data-name="unlink">删除链接</button>
<script>
var btns = document.getElementsByTagName('button');
for(var i = 0; i < btns.length; i++){
btns[i].onclick = function(){
document.execCommand(this.getAttribute('data-name'),false,this.getAttribute('data-value'));
}
}
</script>
选区
【getSelection()】
在富文本编辑器中,使用getSelection()方法,可以确定实际选择的文本。这个方法是window对象和document对象的属性,调用它会返回一个表示当前选择文本的Selection对象。每个Selection对象都有下列属性
[注意]IE8-浏览器不支持
anchorNode:选区起点所在的节点
anchorOffset:在到达选区起点位置之前跳过的anchorNode中的字符数量
focusNode:选区终点所在的节点
focusOffset:focusNode中包含在选区之内的字符数量
isCollapsed:布尔值,表示选区的起点和终点是否重合
rangeCount:选区中包含的DOM范围的数量
Selection对象的这些属性并没有包含多少有用的信息。好在,该对象的下列方法提供了更多信息,并且支持对选区的操作
addRange(range):将指定的DOM范围添加到选区中
collapse(node,offset):将选区折叠到指定节点中的相应的文本偏移位置
collapseToEnd():将选区折叠到终点位置
collapseToStart():将选区折叠到起点位置
containsNode(node):确定指定的节点是否包含在选区中
deleteFromDocument():从文档中删除选区中的文本,与document.execCommand("delete",false,null)命令的结果相同
extend(node,offset):通过将focusNode和focusOffset移动到指定的值来扩展选区
getRangeAt(index):返回索引对应的选区中的DOM范围
removeAllRanges():从选区中移除所有DOM范围。实际上,这样会移除选区,因为选区中至少要有一个范围
reomveRange(range):从选区中移除指定的DOM范围
selectAllChildren(node):清除选区并选择指定节点的所有子节点
toString():返回选区所包含的文本内容
Selection对象的这些方法都极为实用,它们利用了DOM范围来管理选区。由于可以直接操作选择文本的DOM表现,因此访问DOM范围与使用execCommand()相比,能够对富文本编辑器进行更加细化的控制。下面来看一个例子
var selection = document.getSelection();
//取得选择的文本
var selectionText = selection.toString();
//取得代表选区的范围
var range = selection.getRangeAt();
//突出显示选择的文本
var span = document.createElement("span");
span.style.backgroundColor = "yellow";
range.surroundContents(span);
以上代码会为富文本编辑器中被选择的文本添加黄色的背景。这里使用了默认选区中的DOM范围,通过surroundContents()方法将选区添加到了带有黄色背景的<span>元素中
HTML5将getSelection()方法纳入了标准,IE8-浏览器不支持DOM范围,但可以通过它支持的selection对象操作选择的文本。IE中的selection对象是document的属性,要取得富文本编辑器中选择的文本,首先必须创建一个文本范围,然后再像下面这样访问其text属性
var range = document.selection.createRange();
var selectedText = range.text;
虽然使用IE的文本范围来执行HTML操作并不像使用DOM范围那么可靠,但也不失为一种有效的途径。要像前面使用DOM范围那样实现相同的文本高亮效果,可以组合使用htmlText属性和pasteHTML()方法
var range = document.selection.createRange();
range.pasteHTML("<span style=\"background-color:yellow\">" + range.htmlText+"</span>");
以上代码通过htmlText取得了当前选区中的HTML,然后将其放在了一对<span>标签中,最后又使用pasteHTML()将结果重新插入到了选区中
表单提交
因为富文本编辑不是使用表单控件实现的,因此富文本编辑器中的HTML不会被自动提交给服务器,而需要手工来提取并提交HTML。为此,通常可以添加一个隐藏的表单字段,让它的值等于从iframe或使用contenteditable属性的元素中提取出的HTML。具体来说,就是在提交表单之前提取出HTML,并将其插入到隐藏的字段中。下面就是通过表单的onsubmit事件处理程序实现上述操作的代码
form.onsubmit = function(e){
e = e || event;
var target = e.target || e.srcElement;
target.elements["comments"].value = frames["richedit"].document.body.innerHTML;
}
在此,通过文档主体的innerHTML属性取得了iframe中的HTML,然后将其插入到了名为"comments"的表单字段中。这样可以确保恰好在提交表单之前填充"comments"字段。如果在代码中通过submit()来手工提交表单,那么一定不要忘记事先执行上面的操作。对于contenteditable元素,也可以执行类似操作
form.onsubmit = function(e){
e = e || event;
var target = e.target || e.srcElement;
target.elements["comments"].value = document.getElementById('wysiwyg').innerHTML;
}
最后
实现一个富文本编辑器,看似容易,但实际上是一个大工程
给大家推荐几款不错的在线富文本编辑器
欢迎交流
深入理解javascript中的富文本编辑的更多相关文章
- 理解JavaScript中的原型继承(2)
两年前在我学习JavaScript的时候我就写过两篇关于原型继承的博客: 理解JavaScript中原型继承 JavaScript中的原型继承 这两篇博客讲的都是原型的使用,其中一篇还有我学习时的错误 ...
- 深入理解JavaScript中创建对象模式的演变(原型)
深入理解JavaScript中创建对象模式的演变(原型) 创建对象的模式多种多样,但是各种模式又有怎样的利弊呢?有没有一种最为完美的模式呢?下面我将就以下几个方面来分析创建对象的几种模式: Objec ...
- 深入理解JavaScript中的属性和特性
深入理解JavaScript中的属性和特性 JavaScript中属性和特性是完全不同的两个概念,这里我将根据自己所学,来深入理解JavaScript中的属性和特性. 主要内容如下: 理解JavaSc ...
- 深入理解javascript中执行环境(作用域)与作用域链
深入理解javascript中执行环境(作用域)与作用域链 相信很多初学者对与javascript中的执行环境与作用域链不能很好的理解,这里,我会按照自己的理解同大家一起分享. 一般情况下,我们把执行 ...
- 【干货理解】理解javascript中实现MVC的原理
理解javascript中的MVC MVC模式是软件工程中一种软件架构模式,一般把软件模式分为三部分,模型(Model)+视图(View)+控制器(Controller); 模型:模型用于封装与应用程 ...
- 理解javascript中的策略模式
理解javascript中的策略模式 策略模式的定义是:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换. 使用策略模式的优点如下: 优点:1. 策略模式利用组合,委托等技术和思想,有效 ...
- 深入理解javascript中的立即执行函数(function(){…})()
投稿:junjie 字体:[增加 减小] 类型:转载 时间:2014-06-12 我要评论 这篇文章主要介绍了深入理解javascript中的立即执行函数,立即执行函数也叫立即调用函数,通常它的写法是 ...
- 转载 深入理解JavaScript中的this关键字
转载原地址: http://www.cnblogs.com/rainman/archive/2009/05/03/1448392.html 深入理解JavaScript中的this关键字 1. 一 ...
- js架构设计模式——理解javascript中的MVVM开发模式
理解javascript中的MVVM开发模式 http://blog.csdn.net/slalx/article/details/7856769 MVVM的全称是Model View ViewMod ...
随机推荐
- Leetcode Implement Queue using Stacks
Implement the following operations of a queue using stacks. push(x) -- Push element x to the back of ...
- 算法_bitmap算法
概述 所谓bitmap就是用一个bit位来标记某个元素对应的value,而key即是这个元素.由于采用bit为单位来存储数据,因此在可以大大的节省存储空间 算法思想 32位机器上,一个整形,比 ...
- python RecursionError: maximum recursion depth exceeded in comparison错误
处理快速排序,递归深度可能非常大,而系统默认的深度可能没有这么大 需要设置最大递归深度 import sys sys.setrecursionlimit(100000) # 这个值的大小取决你自己,最 ...
- 【Beta】Daily Scrum Meeting第六次
1.任务进度 学号 已完成 接下去要做 502 无 发布任务到服务器 509 给所有api添加注释 将各个api改为面向对象 517 无 删除任务的控件及逻辑 530 下拉刷新控件 添加及修改职工信息 ...
- Java Hashtable的实现
先附源码: package java.util; import java.io.*; /** * This class implements a hash table, which maps keys ...
- input在标签内设置禁止输入空格
1.通过正则匹配解决问题 此处涉及\s:匹配任意空白符 \S:匹配任意非空白字符 <input type="text" onkeyup="this.value=t ...
- 书中的银行,我们一起奋斗的C#,只因乐在其中~
梦回C#,只因心中还留有那么一点执着,相信大家应该也有这些感触吧!!所以呢?我想给大伙分享我的一些学习,也希望大家能给我多点建议,让我们一起进步,共同成长!!! 那么我们就来看一下该怎么运 ...
- Vuforia点击屏幕自动对焦,过滤UGUI的按钮
//点击屏幕自对对焦 #if UNITY_EDITOR )) #elif UNITY_ANDROID || UNITY_IPHONE && Input.GetTouch().phase ...
- 基于Picture Library创建的图片文档库中的上传多个文件功能(upload multiple files)报错怎么解决?
复现过程 首先,我创建了一个基于Picture Library的图片文档库,名字是 Pic Lib 创建完毕后,我点击它的Upload 下拉菜单,点击Upload Picture按钮 在弹出的对话框中 ...
- POS与EPOS区别
本文分文三个部分来介绍,第一部分是两个概念(POS与EPOS),第三部分是 POS与EPOS的区别. 一.epos ( electronic point of sale ) 俗称电话pos机: .EP ...