14.5 富文本编辑【JavaScript高级程序设计第三版】

富文本编辑,又称为WYSIWYG(What You See Is What You Get,所见即所得)。在网页中编辑富文本内容,是人们对Web 应用程序最大的期待之一。虽然也没有规范,但在IE 最先引入的这一功能基础上,已经出现了事实标准。并且,Opera、Safari、Chrome 和Firefox 都已经支持这一功能。这一技术的本质,就是在页面中嵌入一个包含空HTML 页面的iframe。经过设置designMode 属性,这个空白的HTML 页面能够被编辑,而编辑对象则是该页面<body>元素的HTML 代码。designMode 属性有两个可能的值:"off"(默认值)和"on"。在设置为"on"时,整个文档都会变得能够编辑(显示插入符号),而后就能够像使用字处理软件同样,经过键盘将文本内容加粗、变成斜体,等等。
能够给iframe 指定一个很是简单的HTML 页面做为其内容来源。例如:javascript

<!DOCTYPE html>
<html>
<head>
<title>Blank Page for Rich Text Editing</title>
</head>
<body>
</body>
</html>

这个页面在iframe 中能够像其余页面同样被加载。要让它能够编辑,必需要将designMode 设置为"on",但只有在页面彻底加载以后才能设置这个属性。所以,在包含页面中,须要使用onload 事件处理程序来在恰当的时刻设置designMode,以下面的例子所示:html

<iframe name="richedit" style="height:100px;width:100px;" src="blank.htm">
</iframe>
<script type="text/javascript">
	EventUtil.addHandler(window, "load",
	function() {
		frames["richedit"].document.designMode = "on";
	});
</script>

等到以上代码执行以后,你就会在页面中看到一个相似文本框的可编辑区字段。这个区字段具备与其余网页相同的默认样式;不过,经过为空白页面应用CSS 样式,能够修改可编辑区字段的外观。java

14.5.1 使用contenteditable属性

另外一种编辑富文本内容的方式是使用名为contenteditable 的特殊属性,这个属性也是由IE 最先实现的。能够把contenteditable 属性应用给页面中的任何元素,而后用户当即就能够编辑该元素。
这种方法之因此受到欢迎,是由于它不须要iframe、空白页和JavaScript,只要为元素设置contenteditable 属性便可。node

<div class="editable" id="richedit" contenteditable></div>

这样,元素中包含的任何文本内容就均可以编辑了,就好像这个元素变成了<textarea>元素同样。
经过在这个元素上设置contenteditable 属性,也能打开或关闭编辑模式。api

var div = document.getElementById("richedit");
div.contentEditable = "true";

contenteditable 属性有三个可能的值:"true"表示打开、"false"表示关闭,"inherit"表示从父元素那里继承(由于能够在contenteditable 元素中建立或删除元素)。支持contenteditable属性的元素有IE、Firefox、Chrome、Safari 和Opera。在移动设备上,支持contenteditable 属性的浏览器有iOS 5+中的Safari 和Android 3+中的WebKit。浏览器

14.5.2 操做富文本

与富文本编辑器交互的主要方式,就是使用document.execCommand()。这个方法能够对文档执行预约义的命令,并且能够应用大多数格式。能够为document.execCommand()方法传递3 个参数:要执行的命令名称、表示浏览器是否应该为当前命令提供用户界面的一个布尔值和执行命令必须的一个值(若是不须要值,则传递null)。为了确保跨浏览器的兼容性,第二个参数应该始终设置为false,由于Firefox 会在该参数为true 时抛出错误。服务器

不一样浏览器支持的预约义命令也不同。下表列出了那些被支持最多的命令。框架

 

其中,与剪贴板有关的命令在不一样浏览器中的差别极大。Opera 根本没有实现任何剪贴板命令,而Firefox 在默认状况下会禁用它们(必须修改用户的首选项来启用它们)。Safari 和Chrome 实现了cut 和copy,但没有实现paste。不过,即便不能经过document.execCommand()来执行这些命令,但却能够经过相应的快捷键来实现一样的操做。
能够在任什么时候候使用这些命令来修改富文本区域的外观,以下面的例子所示。编辑器

//转换粗体文本
frames["richedit"].document.execCommand("bold", false, null);
//转换斜体文本
frames["richedit"].document.execCommand("italic", false, null);
//建立指向www.wrox.com 的连接
frames["richedit"].document.execCommand("createlink", false,
"http://www.wrox.com");
//格式化为1 级标题
frames["richedit"].document.execCommand("formatblock", false, "<h1>");

运行一下spa

一样的方法也适用于页面中contenteditable 属性为"true"的区块,只要把对框架的引用替换成当前窗口的document 对象便可。

//转换粗体文本
document.execCommand("bold", false, null);
//转换斜体文本
document.execCommand("italic", false, null);
//建立指向www.wrox.com 的连接
document.execCommand("createlink", false,
"http://www.wrox.com");
//格式化为1 级标题
document.execCommand("formatblock", false, "<h1>");

运行一下
须要注意的是,虽然全部浏览器都支持这些命令,但这些命令所产生的HTML 仍然有很大不一样。例如,执行bold 命令时,IE 和Opera 会使用<strong>标签包围文本,Safari 和Chrome 使用<b>标签,而Firefox 则使用<span>标签。因为各个浏览器实现命令的方式不一样,加上它们经过innerHTML 实现转换的方式也不同,所以不能期望富文本编辑器会产生一致的HTML。
除了命令以外,还有一些与命令相关的方法。第一个方法就是queryCommandEnabled(),能够用它来检测是否能够针对当前选择的文本,或者当前插入字符所在位置执行某个命令。这个方法接收一个参数,即要检测的命令。若是当前编辑区域容许执行传入的命令,这个方法返回true,不然返回false。例如:

var result = frames["richedit"].document.queryCommandEnabled("bold");

若是可以对当前选择的文本执行"bold"命令,以上代码会返回true。须要注意的是,query-CommandEnabled()方法返回true,并不意味着实际上就能够执行相应命令,而只能说明对当前选择的文本执行相应命令是否合适。例如,Firefox 在默认状况下会禁用剪切操做,但执行queryCommand-Enabled("cut")也可能会返回true。
另外,queryCommandState()方法用于肯定是否已将指定命令应用到了选择的文本。例如,要肯定当前选择的文本是否已经转换成了粗体,可使用以下代码。

var isBold = frames["richedit"].document.queryCommandState("bold");

运行一下
若是此前已经对选择的文本执行了"bold"命令,那么上面的代码会返回true。一些功能全面的富文本编辑器,正是利用这个方法来更新粗体、斜体等按钮的状态的。
最后一个方法是queryCommandValue(),用于取得执行命令时传入的值(即前面例子中传给document.execCommand()的第三个参数)。例如,在对一段文本应用"fontsize"命令时若是传入了7,那么下面的代码就会返回"7":

var fontSize = frames["richedit"].document.queryCommandValue("fontsize");

运行一下
经过这个方法能够肯定某个命令是怎样应用到选择的文本的,能够据以肯定再对其应用后续命令是否合适。

14.5.3 富文本选区

在富文本编辑器中,使用框架(iframe)的getSelection()方法,能够肯定实际选择的文本。
这个方法是window 对象和document 对象的属性,调用它会返回一个表示当前选择文本的Selection对象。每一个Selection 对象都有下列属性。

  • 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 对象的这些方法都极为实用,它们利用了(第12 章讨论过的)DOM范围来管理选区。
因为能够直接操做选择文本的DOM 表现,所以访问DOM范围与使用execCommand()相比,可以对富文本编辑器进行更加细化的控制。下面来看一个例子。

var selection = frames["richedit"].getSelection();
//取得选择的文本
var selectedText = selection.toString();
//取得表明选区的范围
var range = selection.getRangeAt(0);
//突出显示选择的文本
var span = frames["richedit"].document.createElement("span");
span.style.backgroundColor = "yellow";
range.surroundContents(span);

运行一下
以上代码会为富文本编辑器中被选择的文本添加黄色的背景。这里使用了默认选区中的DOM 范围,经过surroundContents()方法将选区添加到了带有黄色背景的<span>元素中。
HTML5 将getSelection()方法归入了标准,并且IE九、Firefox、Safari、Chrome 和Opera 8 都实现了它。因为历史缘由,在Firefox 3.6+中调用document.getSelection()会返回一个字符串。为此,能够在Firefox 3.6+中改做调用window.getSelection(),从而返回selection 对象。Firefox 8 修复了document.getSelection()的bug,能返回与window.getSelection()相同的值。
IE8 及更早的版本不支持DOM范围,但咱们能够经过它支持的selection 对象操做选择的文本。
IE 中的selection 对象是document 的属性,本章前面曾经讨论过。要取得富文本编辑器中选择的文本,首先必须建立一个文本范围(请参考第12 章中的相关内容),而后再像下面这样访问其text 属性。

var range = frames["richedit"].document.selection.createRange();
var selectedText = range.text;

虽然使用IE 的文本范围来执行HTML 操做并不像使用DOM 范围那么可靠,但也不失为一种有效的途径。要像前面使用DOM 范围那样实现相同的文本高亮效果,能够组合使用htmlText 属性和pasteHTML()方法。

var range = frames["richedit"].document.selection.createRange();
range.pasteHTML("<span style=\"background-color:yellow\"> " + range.htmlText +"</span>");

以上代码经过htmlText 取得了当前选区中的HTML,而后将其放在了一对<span>标签中,最后又使用pasteHTML()将结果从新插入到了选区中。

14.5.4 表单与富文本

因为富文本编辑是使用iframe 而非表单控件实现的,所以从技术上说,富文本编辑器并不属于表单。换句话说,富文本编辑器中的HTML 不会被自动提交给服务器,而须要咱们手工来提取并提交HTML。为此,一般能够添加一个隐藏的表单字段,让它的值等于从iframe 中提取出的HTML。具体来讲,就是在提交表单以前,从iframe 中提取出HTML,并将其插入到隐藏的字段中。下面就是经过表单的onsubmit 事件处理程序实现上述操做的代码。

EventUtil.addHandler(form, "submit",
function(event) {
	event = EventUtil.getEvent(event);
	var target = EventUtil.getTarget(event);
	target.elements["comments"].value = frames["richedit"].document.body.innerHTML;
});

运行一下
在此,咱们经过文档主体的innerHTML 属性取得了iframe 中的HTML,而后将其插入到了名为"comments"的表单字段中。这样能够确保刚好在提交表单以前填充"comments"字段。若是你想在代码中经过submit()来手工提交表单,那么必定不要忘记事先执行上面的操做。对于contenteditable元素,也能够执行相似操做。

EventUtil.addHandler(form, "submit",
function(event) {
	event = EventUtil.getEvent(event);
	var target = EventUtil.getTarget(event);
	target.elements["comments"].value = document.getElementById("richedit").innerHTML;
});

下载离线版教程:http://www.shouce.ren/api/view/a/15218

相关文章
相关标签/搜索