您的位置:首页 > 其它

一步步教你实现富文本编辑器(第三部分)

2009-08-02 17:44 239 查看
loadEvent(function(){
guarder();
})

#div_digg{display:none!important;}

这部分我们把富文本编辑器的代码打包成一个类。至于如何实现没有什么好说的,就是那五种方案,我取用的是原型,那是最JS,也是最ruby的。我们的所有实现都在原型进行,最后new出来就是!构造函数有一个必选参数,就是那个textarea的id,其他都是动态生成的,包括其样式。关于样式,我已提供了一个很好用的addSheet函数了。那么开始吧,我们要尽快做出第二部分最后阶段的样式再说!

首先我为大家提供了一个模板,大家可以根据它自行完成我们讲过的部分。

var Class = {
create: function() {
return function() {
this.initialize.apply(this, arguments);
}
}
}
var extend = function(destination, source) {
for (var property in source) {
destination[property] = source[property];
}
return destination;
}
var RichTextEditor =  Class.create();//我们的富文本编辑器类
RichTextEditor.prototype = {
initialize:function(options){
this.setOptions(options);
this.drawEditor(this.options.textarea_id);
},
setOptions:function(options){
this.options = { //这里集中设置默认属性
id:'jeditor_'+ new Date().getTime(),
textarea_id:null//用于textarea的ID,也就是我们的必选项
}
extend(this.options, options || {});//这里是用来重写默认属性
},
ID:function(id){return document.getElementById(id) },//getElementById的快捷方式
TN:function(tn){ return document.getElementsByTagName(tn) },//getElementsByTagName的快捷方式
CE:function(s){ return document.createElement(s)},//createElement的快捷方式
drawEditor:function(id){
var textarea = this.ID(id);
textarea.style.display = "none";
}
}


<!doctype html>
<html dir="ltr" lang="zh-CN">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<script type="text/javascript">var Class = { create: function() { return function() { this.initialize.apply(this, arguments); } } } var extend = function(destination, source) { for (var property in source) { destination[property] = source[property]; } return destination; } var RichTextEditor = Class.create();//我们的富文本编辑器类 RichTextEditor.prototype = { initialize:function(options){ this.setOptions(options); this.drawEditor(this.options.textarea_id); }, setOptions:function(options){ this.options = { //这里集中设置默认属性 id:'jeditor_'+ new Date().getTime(), textarea_id:null//用于textarea的ID,也就是我们的必选项 } extend(this.options, options || {});//这里是用来重写默认属性 }, ID:function(id){return document.getElementById(id) },//getElementById的快捷方式 TN:function(tn){ return document.getElementsByTagName(tn) },//getElementsByTagName的快捷方式 CE:function(s){ return document.createElement(s)},//createElement的快捷方式 drawEditor:function(id){ var textarea = this.ID(id); textarea.style.display = "none"; } }window.onload = function(){
new RichTextEditor({
id:'editor',
textarea_id:'textarea'
});
}
</script>
<title>富文本编辑器</title>
</head>
<body>
<h1>这是模板</h1>
<form action="#">
<textarea id="textarea"></textarea>
</form>
</body>
</html>

运行代码

接着下来我们基本就是在drawEditor这个函数工作了,我们隐藏了原来的textarea后,然后在其下面生成一个div,当作富本文编辑器的工具栏,然后再在其下面生成iframe,这是我们富文本编辑器的工作区。工具栏的按钮很多,我们把这些按钮的名字以及隐藏在title的命令全部打包在一个对象,然后循环生成它们,并在循环中设置样式与绑定事件。这些和第二部分所讲的别无二致,我就不重复了,快手净脚搞出它们吧!

var buttons = {
'fontname': {
'宋体':'SimSun',
'隶书':'LiSu',
'楷体':'KaiTi_GB2312',
'幼圆':'YouYuan',
'黑体':'SimHei',
'雅黑':'Microsoft YaHei',
'仿宋':'FangSong',
'Comic Sans MS':'Comic Sans MS'
},
'fontsize': {
'特小': 1,
'很小': 2,
'小': 3,
'中': 4,
'大': 5,
'很大': 6,
'特大':7
},
'removeformat':'还原',
'bold': '加粗',
'italic': '斜体',
'underline': '下划线',
'strikethrough':'删除线',
'justifyleft': '居左',
'justifycenter': '居中',
'justifyright': '居右',
'indent':'缩进',
'outdent':'悬挂',
'forecolor':'前景色',
'backcolor':'背景色',
'createlink': '超链接',
'insertimage': '插图',
'insertorderedlist':'有序列表',
'insertunorderedlist':'无序列表',
'html':'查看'
};


<!doctype html>
<html dir="ltr" lang="zh-CN">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<style type="text/css">
#textarea {width:600px;height:300px;background:#F2F1D7;}
</style>
<script type="text/javascript">
var addSheet = function(){
var doc,cssCode;
if(arguments.length == 1){
doc = document;
cssCode = arguments[0];
}else if(arguments.length == 2){
doc = arguments[0];
cssCode = arguments[1];
}else{
alert("addSheet函数最多接受两个参数!");
}
var headElement = doc.getElementsByTagName("head")[0];
var styleElements = headElement.getElementsByTagName("style");
if(styleElements.length == 0){/*如果不存在style元素则创建*/
if(!+"\v1"){ //ie
doc.createStyleSheet();
}else{
var tempStyleElement = doc.createElement('style');//w3c
tempStyleElement.setAttribute("type", "text/css");
headElement.appendChild(tempStyleElement);
}
}
var styleElement = styleElements[0];
var media = styleElement.getAttribute("media");
if(media != null && !/screen/.test(media.toLowerCase()) ){
styleElement.setAttribute("media","screen");
}
if(!+"\v1"){ //ie
styleElement.styleSheet.cssText += cssCode;
}else if(/a/[-1]=='a'){
styleElement.innerHTML += cssCode;//火狐支持直接innerHTML添加样式表字串
}else{
styleElement.appendChild(doc.createTextNode(cssCode))
}
}
var Class = {
create: function() {
return function() {
this.initialize.apply(this, arguments);
}
}
}
var extend = function(destination, source) {
for (var property in source) {
destination[property] = source[property];
}
return destination;
}

var RichTextEditor = Class.create();//我们的富文本编辑器类
RichTextEditor.prototype = {
initialize:function(options){
this.setOptions(options);
this.drawEditor(this.options.textarea_id);
},
setOptions:function(options){
this.options = { //这里集中设置默认属性
id:'jeditor_'+ new Date().getTime(),
textarea_id:null//用于textarea的ID,也就是我们的必选项
}
extend(this.options, options || {});//这里是用来重写默认属性
},
ID:function(id){return document.getElementById(id) },//getElementById的快捷方式
TN:function(tn){ return document.getElementsByTagName(tn) },//getElementsByTagName的快捷方式
CE:function(s){ return document.createElement(s)},//createElement的快捷方式
drawEditor:function(id){
var $ = this,
textarea = this.ID(id),
toolbar = this.CE('div'),
br = this.CE('br'),//用于清除浮动
iframe = this.CE('iframe');
textarea.style.display = "none";
textarea.parentNode.insertBefore(toolbar,textarea);
textarea.parentNode.insertBefore(br,textarea);
textarea.parentNode.insertBefore(iframe,textarea);
br.style.cssText = "clear:both";
toolbar.setAttribute("id","RTE_toolbar");
iframe.setAttribute("id","RTE_iframe");
iframe.frameBorder=0;
var iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
iframeDocument.designMode = "on";
iframeDocument.open();
iframeDocument.write('<html><head><style type="text/css">body{ font-family:arial; font-size:13px;background:#DDF3FF;border:0; }</style></head></html>');
iframeDocument.close();
var buttons = {//工具栏的按钮集合
'removeFormat':'还原',
'bold': '加粗',
'italic': '斜体',
'underline': '下划线',
'strikethrough':'删除线',
'justifyleft': '居左',
'justifycenter': '居中',
'justifyright': '居右',
'indent':'缩进',
'outdent':'悬挂',
'forecolor':'前景色',
'backcolor':'背景色',
'createlink': '超链接',
'insertimage': '插图',
'fontname': {
'宋体': 'SimSun',
'隶书':'LiSu',
'楷体':'KaiTi_GB2312',
'幼圆':'YouYuan',
'黑体': 'SimHei',
'雅黑': 'Microsoft YaHei',
'仿宋':'FangSong',
'Comic Sans MS':'Comic Sans MS'
},
'fontsize': {
'特小': 1,
'很小': 2,
'小': 3,
'中': 4,
'大': 5,
'很大': 6,
'特大':7
},
'insertorderedlist':'有序列表',
'insertunorderedlist':'无序列表',
'html':'查看'
};
var buttonClone = $.CE("a"),
fragment = document.createDocumentFragment(),
switchEditMode = true;
buttonClone.className = 'button'
for (var i in buttons){//添加命令按钮的名字,样式
if (i == 'fontname' || i == 'fontsize'){
var select = $.CE('select');
select.setAttribute("title",i);
for (var ii in buttons[i]){
var option = new Option(ii, buttons[i][ii]);
if (!+"\v1"){
select.add(option);
}else{
select.appendChild(option);
}
}
select.onchange = new function(){
return function(){
var command = select.getAttribute("title"),
value = select.options[select.selectedIndex].value;
if(command == 'backcolor' && /a/[-1]=='a'){
iframeDocument.execCommand('hilitecolor',false,value);
}else{
iframeDocument.execCommand(command,false,value);
}
};
};
fragment.appendChild(select);
} else {
var button = buttonClone.cloneNode("true");
if(i == 'backcolor'){//特殊处理背景色按钮,
if (!+"\v1"){
button.setAttribute("title","background")
}else{
button.setAttribute("title","hilitecolor")
}
}
button.setAttribute("title",i);
button.innerHTML = buttons[i];
button.setAttribute("unselectable", "on");
button.onclick = new function(){
var command = button.getAttribute("title");
return function(){
switch(command){
case 'createlink':
case 'insertimage':
var value = prompt('请输入超链接:', 'http://');
iframeDocument.execCommand(command,false,value);
break;
case "html"://查看源码
if(switchEditMode){//切换到textarea
iframe.style.display = "none";
textarea.style.display = "block";
textarea.value = iframeDocument.body.innerHTML;
textarea.focus();
switchEditMode = false;
}else{//切换到iframe
iframe.style.display = "block";
textarea.style.display = "none";
iframeDocument.body.innerHTML = textarea.value;
iframe.contentWindow.focus();
switchEditMode = true;
}
break;
default:
iframeDocument.execCommand(command,false,'');
}
iframe.contentWindow.focus();
}
}
fragment.appendChild(button);
}
}
toolbar.appendChild(fragment);
addSheet('\
#RTE_iframe{width:600px;height:300px;}\
#RTE_toolbar{float:left;width:600px;background:#D5F3F4;}\
#RTE_toolbar .button{display:block;float:left;border:1px solid #CCC;margin-left:5px;\
color:#000;background:#D0E8FC;height:20px;text-align:center;padding:0 10px;white-space: nowrap;}\
#RTE_toolbar select{float:left;height:20px;width:60px;margin-right:5px;}\
#RTE_toolbar .button:hover{color:#fff;border-color:#fff #aaa #aaa #fff;}');
}
}
window.onload = function(){
new RichTextEditor({
id:'editor',
textarea_id:'textarea'
});
}
</script>
<title>富文本编辑器</title>
</head>
<body>
<form action="#">
<textarea id="textarea" wrap="on"></textarea>
</form>
</body>
</html>

运行代码

到这里,基本和第二部分差不多了。至于前景色与背景色,我们打算用我以前提供过的颜色选择器实现,现在我们的目标是那两个下拉选择框。我觉得那两个select太不人性化了,由于其级别很高,我们很难对它进行制定。作为可见即可得,我们要来在拉动那个select时,应该能给人们一个大概样子。因此select必须死。

我们修改buttons对象,把fontname与fontsize提取出来单独处理!

var fontFamilies = ['宋体','经典中圆简','微软雅黑', '黑体', '楷体', '隶书', '幼圆',
'Arial', 'Arial Narrow', 'Arial Black', 'Comic Sans MS',
'Courier New', 'Georgia', 'New Roman Times', 'Verdana']
var fontSizes= [[1, 'xx-small', '最小'],
[2, 'x-small', '特小'],
[3, 'small', '小'],
[4, 'medium', '中'],
[5, 'large', '大'],
[6, 'x-large', '特大'],
[7, 'xx-large', '最大']];

但是这样一来,我们原来的事件绑定机制就遭到灭顶之灾!我们必须奠出我们的addEvent函数。addEvent要求我们传入三个参数(需要绑定的元素,事件类型与绑定事件),后两个很明确了,问题是第一个,我们怎么找到这些元素呢?不过一个个加id吧。不用,我们在最开始的循环就把这些元素加入一个数组就是!

for (var i in buttons){/*添加命令按钮的名字,样式*/
var button = buttonClone.cloneNode("true");
if(i == 'backcolor'){/*特殊处理背景色按钮*/
if (!+"\v1"){
button.setAttribute("title","background")
}else{
button.setAttribute("title","hilitecolor")
}
}
button.setAttribute("title",i);/*把execCommand的命令参数放到title*/
button.innerHTML = buttons[i];
button.setAttribute("unselectable", "on");/*防止焦点转移到点击的元素上,从而保证文本的选中状态*/
toolbar[i] = button;   /*★★★★把元素放进一个数组,用于事件绑定!★★★★*/
fragment.appendChild(button);
}
toolbar.appendChild(fragment);
}

得益于javascript的事件机制,我们只对toolbar进行监听,就可以监听其所有子元素。另外,我们把格式化命令独立出来,简化我们的程序。

this.addEvent(toolbar, 'click', function(){
var e = arguments[0] || window.event,
target = e.srcElement ? e.srcElement : e.target,
command = target.getAttribute("title");
switch (command){
case 'createlink':
case 'insertimage':
var value = prompt('请输入网址:', 'http://');
_format(command,value);
break;
case 'fontname'://这几个特殊处理
case 'fontsize':
case 'forecolor':
case 'backcolor':
case 'html':
return;
default:
_format(command,'');
break;
}
});

var _format = function(x,y){//内部私有函数,处理富文本编辑器的格式化命令
iframeDocument.execCommand(x,false,y);
iframe.contentWindow.focus();
}

至于字体与字码,我们可以用div模拟select了!然后为它们绑定两个事件,一个是用来显示隐藏select,一个是用来执行格式化命令。

var fontPicker = $.CE('div');
fontPicker.setAttribute('unselectable', 'on');
fontPicker.className = "fontpicker";
toolbar.appendChild(fontPicker);//字体选择器与字码选择器都是共用一个虚拟select
$.addEvent(toolbar['fontname'], 'click', function(){
//根据情况选择载入虚拟select的内容
})

$.addEvent(toolbar['fontsize'], 'click', function(){
//根据情况选择载入虚拟select的内容
})
var bind_select_event = function(button,picker){
//显示或隐藏文字选择器
}
/************************用于生成文字选择器的内容************************/
fontPickerHtml:function(type,array){
var builder = [];
for(var i = 0,l = array.length;i<l;i++){
builder.push('<a unselectable="on" style="');
if(type == 'fontname'){
builder.push('font-family');
builder.push(':\'');
builder.push(array[i]); /*呈现一行(一行就是一种字体)*/
builder.push('\';" href="javascript:void(0)">');
builder.push(array[i]);
}else if(type == 'fontsize'){
builder.push('font-size'); /*呈现一行(一行就是一种字号)*/
builder.push(':');
builder.push(array[i][1]);
builder.push(';" sizevalue="');
builder.push(array[i][0]);
builder.push('" href="javascript:void(0)">');//IE的a元素必须有href才有悬浮效果
builder.push(array[i][2]);
}
builder.push("</a>");
}
return builder.join('');
}


<!doctype html>
<html dir="ltr" lang="zh-CN">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<style type="text/css">
#textarea {width:600px;height:300px;background:#F2F1D7;}
</style>
<script type="text/javascript">
var addSheet = function(){
var doc,cssCode;
if(arguments.length == 1){
doc = document;
cssCode = arguments[0];
}else if(arguments.length == 2){
doc = arguments[0];
cssCode = arguments[1];
}else{
alert("addSheet函数最多接受两个参数!");
}
var headElement = doc.getElementsByTagName("head")[0];
var styleElements = headElement.getElementsByTagName("style");
if(styleElements.length == 0){/*如果不存在style元素则创建*/
if(!+"\v1"){ //ie
doc.createStyleSheet();
}else{
var tempStyleElement = doc.createElement('style');//w3c
tempStyleElement.setAttribute("type", "text/css");
headElement.appendChild(tempStyleElement);
}
}
var styleElement = styleElements[0];
var media = styleElement.getAttribute("media");
if(media != null && !/screen/.test(media.toLowerCase()) ){
styleElement.setAttribute("media","screen");
}
if(!+"\v1"){ //ie
styleElement.styleSheet.cssText += cssCode;
}else if(/a/[-1]=='a'){
styleElement.innerHTML += cssCode;//火狐支持直接innerHTML添加样式表字串
}else{
styleElement.appendChild(doc.createTextNode(cssCode))
}
}
var Class = {
create: function() {
return function() {
this.initialize.apply(this, arguments);
}
}
}
var extend = function(destination, source) {
for (var property in source) {
destination[property] = source[property];
}
return destination;
}

var RichTextEditor = Class.create();//我们的富文本编辑器类
RichTextEditor.prototype = {
initialize:function(options){
this.setOptions(options);
this.drawEditor(this.options.textarea_id);
},
setOptions:function(options){
this.options = { //这里集中设置默认属性
id:'jeditor_'+ new Date().getTime(),
textarea_id:null//用于textarea的ID,也就是我们的必选项
}
extend(this.options, options || {});//这里是用来重写默认属性
},
ID:function(id){return document.getElementById(id) },//getElementById的快捷方式
TN:function(tn){ return document.getElementsByTagName(tn) },//getElementsByTagName的快捷方式
CE:function(s){ return document.createElement(s)},//createElement的快捷方式
fontPickerHtml:function(type,array){
var builder = [];
for(var i = 0,l = array.length;i<l;i++){
builder.push('<a unselectable="on" style="');
if(type == 'fontname'){
builder.push('font-family');
builder.push(':');
builder.push(array[i]); /*呈现一行(一行就是一种字体)*/
builder.push(';" href="javascript:void(0)">');
builder.push(array[i]);
}else if(type == 'fontsize'){
/*呈现一行(一行就是一种字号)*/
builder.push('font-size');
builder.push(':');
builder.push(array[i][1]);
builder.push(';" sizevalue="');
builder.push(array[i][0]);
builder.push('" href="javascript:void(0)">');
builder.push(array[i][2]);
}
builder.push("</a>");
}
return builder.join('');
},
addEvent:function(el, type, fn ) {
if(!+"\v1") {
el['e'+type+fn]=fn;
el.attachEvent( 'on'+type, function() {
el['e'+type+fn]();
} );
}else{
el.addEventListener( type, fn, false );
}
},
drawEditor:function(id){
var $ = this,
textarea = this.ID(id),
toolbar = this.CE('div'),
br = this.CE('br'),//用于清除浮动
iframe = this.CE('iframe');
textarea.style.display = "none";
textarea.parentNode.insertBefore(toolbar,textarea);
textarea.parentNode.insertBefore(br,textarea);
textarea.parentNode.insertBefore(iframe,textarea);
br.style.cssText = "clear:both";
toolbar.setAttribute("id","RTE_toolbar");
iframe.setAttribute("id","RTE_iframe");
iframe.frameBorder=0;
var iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
iframeDocument.designMode = "on";
iframeDocument.open();
iframeDocument.write('<html><head><style type="text/css">body{ font-family:arial; font-size:13px;background:#DDF3FF;border:0; }</style></head></html>');
iframeDocument.close();

var buttons = {//工具栏的按钮集合
'removeFormat':'还原',
'bold': '加粗',
'italic': '斜体',
'underline': '下划线',
'strikethrough':'删除线',
'justifyleft': '居左',
'justifycenter': '居中',
'justifyright': '居右',
'indent':'缩进',
'outdent':'悬挂',
'forecolor':'前景色',
'backcolor':'背景色',
'createlink': '超链接',
'insertimage': '插图',
'fontname': '字体',
'fontsize': '字码',
'insertorderedlist':'有序列表',
'insertunorderedlist':'无序列表',
'html':'查看'
};var fontFamilies = ['宋体','经典中圆简','微软雅黑', '黑体', '楷体', '隶书', '幼圆', 'Arial', 'Arial Narrow', 'Arial Black', 'Comic Sans MS', 'Courier New', 'Georgia', 'New Roman Times', 'Verdana'] var fontSizes= [[1, 'xx-small', '最小'], [2, 'x-small', '特小'], [3, 'small', '小'], [4, 'medium', '中'], [5, 'large', '大'], [6, 'x-large', '特大'], [7, 'xx-large', '最大']];var buttonClone = $.CE("a"),
fragment = document.createDocumentFragment();
buttonClone.className = 'button';
for (var i in buttons){/*添加命令按钮的名字,样式*/
var button = buttonClone.cloneNode("true");
if(i == 'backcolor'){/*特殊处理背景色按钮*/
if (!+"\v1"){
button.setAttribute("title","background")
}else{
button.setAttribute("title","hilitecolor")
}
}
button.setAttribute("title",i);/*把execCommand的命令参数放到title*/
button.innerHTML = buttons[i];
button.setAttribute("unselectable", "on");/*防止焦点转移到点击的元素上,从而保证文本的选中状态*/
toolbar[i] = button; /*★★★★把元素放进一个数组,用于下一个循环绑定事件!★★★★*/
fragment.appendChild(button);
}
toolbar.appendChild(fragment);
$.addEvent(toolbar, 'click', function(){
var e = arguments[0] || window.event,
target = e.srcElement ? e.srcElement : e.target,
command = target.getAttribute("title");
switch (command){
case 'createlink':
case 'insertimage':
var value = prompt('请输入网址:', 'http://');
_format(command,value);
break;
case 'fontname'://这六个特殊处理,不直接执行fontEdit命令!
case 'fontsize':
case 'forecolor':
case 'backcolor':
case 'html':
return;
default://其他执行fontEdit(cmd, null)命令
_format(command,'');
break;
}
});

var fontPicker = $.CE('div');
fontPicker.setAttribute('unselectable', 'on');
fontPicker.className = "fontpicker";
toolbar.appendChild(fontPicker);
$.addEvent(toolbar['fontname'], 'click', function(){
fontPicker.innerHTML = $.fontPickerHtml('fontname',fontFamilies);
fontPicker.style.width = "150px";
bind_select_event(this,fontPicker);
})
$.addEvent(toolbar['fontsize'], 'click', function(){
fontPicker.innerHTML = $.fontPickerHtml('fontsize',fontSizes);
fontPicker.style.width = "100px";
bind_select_event(this,fontPicker);
})
var bind_select_event = function(button,picker){//显示或隐藏文字选择器
button.style.position = 'relative';
picker.setAttribute("title",button.getAttribute("title")) //转移命令
if(picker.style.display == 'none'){
picker.style.display = 'block';
picker.style.left = button.offsetLeft + 'px';
picker.style.top = (button.clientHeight + button.offsetTop)+ 'px';
}else{
picker.style.display='none';
}
}
$.addEvent(fontPicker,'click',function(){
var e = arguments[0] || window.event,
target = e.srcElement ? e.srcElement : e.target,
command = this.getAttribute("title");
var nn = target.nodeName.toLowerCase();
if(nn == 'a'){
var value;
if('fontsize' == command){
value = target.getAttribute('sizevalue');
}else{
value = target.innerHTML;
}
_format(command,value);
fontPicker.style.display = 'none';
}
});
var _format = function(x,y){//内部私有函数,处理富文本编辑器的格式化命令
try{
iframeDocument.execCommand(x,false,y);
iframe.contentWindow.focus();
}catch(e){}
}

addSheet('\
#RTE_iframe{width:600px;height:300px;}\
#RTE_toolbar{float:left;width:600px;background:#D5F3F4;}\
#RTE_toolbar .button{display:block;float:left;border:1px solid #CCC;margin-left:5px;\
color:#000;background:#D0E8FC;height:20px;text-align:center;padding:0 10px;white-space: nowrap;}\
#RTE_toolbar select{float:left;height:20px;width:60px;margin-right:5px;}\
#RTE_toolbar .button:hover{color:#fff;border-color:#fff #aaa #aaa #fff;}\
div.fontpicker{display:none;height:150px;overflow:auto;position:absolute;\
border:2px solid #c3c9cf;background:#F1FAFA;}\
div.fontpicker a{display:block;text-decoration:none;color:#000;background:#F1FAFA;padding:2px;}\
div.fontpicker a:hover{color:#999;background:#e3e6e9;}');
}
}

window.onload = function(){
new RichTextEditor({
id:'editor',
textarea_id:'textarea'
});
}

</script>
<title>富文本编辑器</title>
</head>
<body>
<form action="#">
<textarea id="textarea" wrap="on"></textarea>
</form>
</body>
</html>

运行代码

上面的代码其实有个问题,不过也可能是IE的问题,在IE中,当我们点击虚拟select的a元素时,execComman函数实际执行了两次,第一次确实是完成了格式化任务,第二次却因为参数为空而报错……囧!我不知道是哪里错了,不过我认为如果我们把事件直接绑定到a元素,而不是绑定到虚拟select的那个div元素,就肯定没问题。不过这样做代码非常复杂非常长,如何定位到这些a元素就要劳师动众一番。我是利用DOM2的事件传播机制缩短它的(嗯,写到这里,我好像明白了一些)。我用了一个很不值得推荐的方法,把execComman放到一个catch块中,吞掉这异常。

更好的办法,我想到了。execCommand之所以执行发两次,是因为IE并没有阻止onclick事件继续向上冒泡,之于为什么会冒泡呢?!这又是个谜了!这是新的代码:

$.addEvent(fontPicker,'click',function(){
/*****************略************/
_format(command,value);
e.cancelBubble = true;//重点
fontPicker.style.display = 'none';
}
});
var _format = function(x,y){//内部私有函数,处理富文本编辑器的格式化命令
//    try{
iframeDocument.execCommand(x,false,y);
iframe.contentWindow.focus();
//    }catch(e){}
}

接着下来是背景色与前景色,以前我就做了一个颜色选择器,具体可参见这篇博文,我就不重复了!流程基本与文字选择器一样,我们这里得修改一下bind_select_event方法。

var bind_select_event = function(button,picker){//显示或隐藏选择器
button.style.position = 'relative';
var command = button.getAttribute("title");
if('backcolor' == command){
command = !+"\v1" ? 'backcolor':'hilitecolor';
}
/************略****************/
}


<!doctype html>
<html dir="ltr" lang="zh-CN">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<style type="text/css">
#textarea {width:600px;height:300px;background:#F2F1D7;}
</style>
<script type="text/javascript">
var addSheet = function(){
var doc,cssCode;
if(arguments.length == 1){
doc = document;
cssCode = arguments[0];
}else if(arguments.length == 2){
doc = arguments[0];
cssCode = arguments[1];
}else{
alert("addSheet函数最多接受两个参数!");
}
var headElement = doc.getElementsByTagName("head")[0];
var styleElements = headElement.getElementsByTagName("style");
if(styleElements.length == 0){/*如果不存在style元素则创建*/
if(!+"\v1"){ //ie
doc.createStyleSheet();
}else{
var tempStyleElement = doc.createElement('style');//w3c
tempStyleElement.setAttribute("type", "text/css");
headElement.appendChild(tempStyleElement);
}
}
var styleElement = styleElements[0];
var media = styleElement.getAttribute("media");
if(media != null && !/screen/.test(media.toLowerCase()) ){
styleElement.setAttribute("media","screen");
}
if(!+"\v1"){ //ie
styleElement.styleSheet.cssText += cssCode;
}else if(/a/[-1]=='a'){
styleElement.innerHTML += cssCode;//火狐支持直接innerHTML添加样式表字串
}else{
styleElement.appendChild(doc.createTextNode(cssCode))
}
}
var Class = {
create: function() {
return function() {
this.initialize.apply(this, arguments);
}
}
}
var extend = function(destination, source) {
for (var property in source) {
destination[property] = source[property];
}
return destination;
}

var RichTextEditor = Class.create();//我们的富文本编辑器类
RichTextEditor.prototype = {
initialize:function(options){
this.setOptions(options);
this.drawEditor(this.options.textarea_id);
},
setOptions:function(options){
this.options = { //这里集中设置默认属性
id:'jeditor_'+ new Date().getTime(),
textarea_id:null//用于textarea的ID,也就是我们的必选项
}
extend(this.options, options || {});//这里是用来重写默认属性
},
ID:function(id){return document.getElementById(id) },//getElementById的快捷方式
TN:function(tn){ return document.getElementsByTagName(tn) },//getElementsByTagName的快捷方式
CE:function(s){ return document.createElement(s)},//createElement的快捷方式
fontPickerHtml:function(type,array){
var builder = [];
for(var i = 0,l = array.length;i<l;i++){
builder.push('<a unselectable="on" style="');
if(type == 'fontname'){
builder.push('font-family');
builder.push(':');
builder.push(array[i]); /*呈现一行(一行就是一种字体)*/
builder.push(';" href="javascript:void(0)">');
builder.push(array[i]);
}else if(type == 'fontsize'){
/*呈现一行(一行就是一种字号)*/
builder.push('font-size');
builder.push(':');
builder.push(array[i][1]);
builder.push(';" sizevalue="');
builder.push(array[i][0]);
builder.push('" href="javascript:void(0)">');
builder.push(array[i][2]);
}
builder.push("</a>");
}
return builder.join('');
},

//用于生成颜色选择器的具体内容
colorPickerHtml : function(){
var _hex = ['FF', 'CC', '99', '66', '33', '00'];
var builder = [];
// 呈现一个颜色格
var _drawCell = function(builder, red, green, blue){
builder.push('<td bgcolor="');
builder.push('#' + red + green + blue);
builder.push('" unselectable="on"></td>');
};
// 呈现一行颜色
var _drawRow = function(builder, red, blue){
builder.push('<tr>');
for (var i = 0; i < 6; ++i) {
_drawCell(builder, red, _hex[i], blue)
}
builder.push('</tr>');
};
// 呈现六个颜色区之一
var _drawTable = function(builder, blue){
builder.push('<table class="cell" unselectable="on">');
for (var i = 0; i < 6; ++i) {
_drawRow(builder, _hex[i], blue)
}
builder.push('</table>');
};
//开始创建
builder.push('<table><tr>');
for (var i = 0; i < 3; ++i) {
builder.push('<td>');
_drawTable(builder, _hex[i]);
builder.push('</td>');
}
builder.push('</tr><tr>');
for (var i = 3; i < 6; ++i) {
builder.push('<td>');
_drawTable(builder, _hex[i])
builder.push('</td>');
}
builder.push('</tr></table>');
builder.push('<table id="color_result"><tr><td id="color_view"></td><td id="color_code"></td></tr></table>');
return builder.join('');
},
addEvent:function(el, type, fn ) {
if(!+"\v1") {
el['e'+type+fn]=fn;
el.attachEvent( 'on'+type, function() {
el['e'+type+fn]();
} );
}else{
el.addEventListener( type, fn, false );
}
},
drawEditor:function(id){
var $ = this,
textarea = this.ID(id),
toolbar = this.CE('div'),
br = this.CE('br'),//用于清除浮动
iframe = this.CE('iframe');
textarea.style.display = "none";
textarea.parentNode.insertBefore(toolbar,textarea);
textarea.parentNode.insertBefore(br,textarea);
textarea.parentNode.insertBefore(iframe,textarea);
br.style.cssText = "clear:both";
toolbar.setAttribute("id","RTE_toolbar");
iframe.setAttribute("id","RTE_iframe");
iframe.frameBorder=0;
var iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
iframeDocument.designMode = "on";
iframeDocument.open();
iframeDocument.write('<html><head><style type="text/css">body{ font-family:arial; font-size:13px;background:#DDF3FF;border:0; }</style></head></html>');
iframeDocument.close();

var buttons = {//工具栏的按钮集合
'removeFormat':'还原',
'bold': '加粗',
'italic': '斜体',
'underline': '下划线',
'strikethrough':'删除线',
'justifyleft': '居左',
'justifycenter': '居中',
'justifyright': '居右',
'indent':'缩进',
'outdent':'悬挂',
'forecolor':'前景色',
'backcolor':'背景色',
'createlink': '超链接',
'insertimage': '插图',
'fontname': '字体',
'fontsize': '字码',
'insertorderedlist':'有序列表',
'insertunorderedlist':'无序列表',
'html':'查看'
};var fontFamilies = ['宋体','经典中圆简','微软雅黑', '黑体', '楷体', '隶书', '幼圆', 'Arial', 'Arial Narrow', 'Arial Black', 'Comic Sans MS', 'Courier New', 'Georgia', 'New Roman Times', 'Verdana'] var fontSizes= [[1, 'xx-small', '最小'], [2, 'x-small', '特小'], [3, 'small', '小'], [4, 'medium', '中'], [5, 'large', '大'], [6, 'x-large', '特大'], [7, 'xx-large', '最大']];var buttonClone = $.CE("a"),
fragment = document.createDocumentFragment();
buttonClone.className = 'button';
for (var i in buttons){/*添加命令按钮的名字,样式*/
var button = buttonClone.cloneNode("true");
if(i == 'backcolor'){/*特殊处理背景色按钮*/
if (!+"\v1"){
button.setAttribute("title","background")
}else{
button.setAttribute("title","hilitecolor")
}
}
button.setAttribute("title",i);/*把execCommand的命令参数放到title*/
button.innerHTML = buttons[i];
button.setAttribute("unselectable", "on");/*防止焦点转移到点击的元素上,从而保证文本的选中状态*/
toolbar[i] = button; /*★★★★把元素放进一个数组,用于下一个循环绑定事件!★★★★*/
fragment.appendChild(button);
}
toolbar.appendChild(fragment);
$.addEvent(toolbar, 'click', function(){
var e = arguments[0] || window.event,
target = e.srcElement ? e.srcElement : e.target,
command = target.getAttribute("title");
switch (command){
case 'createlink':
case 'insertimage':
var value = prompt('请输入网址:', 'http://');
_format(command,value);
break;
case 'fontname'://这六个特殊处理,不直接执行fontEdit命令!
case 'fontsize':
case 'forecolor':
case 'backcolor':
case 'html':
return;
default://其他执行fontEdit(cmd, null)命令
_format(command,'');
break;
}
});
/*******************************************************/
var fontPicker = $.CE('div');
fontPicker.className = "fontpicker";
toolbar.appendChild(fontPicker);
/*******************************************************/
$.addEvent(toolbar['fontname'], 'click', function(){
fontPicker.innerHTML = $.fontPickerHtml('fontname',fontFamilies);
fontPicker.style.width = "150px";
bind_select_event(this,fontPicker);
});
/*******************************************************/
$.addEvent(toolbar['fontsize'], 'click', function(){
fontPicker.innerHTML = $.fontPickerHtml('fontsize',fontSizes);
fontPicker.style.width = "100px";
bind_select_event(this,fontPicker);
});
/*******************************************************/
var bind_select_event = function(button,picker){//显示或隐藏选择器
button.style.position = 'relative';
var command = button.getAttribute("title");
if('backcolor' == command){
command = !+"\v1" ? 'backcolor':'hilitecolor';
}
picker.setAttribute("title",command);//转移命令
if(picker.style.display == 'none'){
picker.style.display = 'block';
picker.style.left = button.offsetLeft + 'px';
picker.style.top = (button.clientHeight + button.offsetTop)+ 'px';
}else{
picker.style.display='none';
}
}
/*******************************************************/
$.addEvent(fontPicker,'click',function(){
var e = arguments[0] || window.event,
target = e.srcElement ? e.srcElement : e.target,
command = this.getAttribute("title");
var nn = target.nodeName.toLowerCase();
if(nn == 'a'){
var value;
if('fontsize' == command){
value = target.getAttribute('sizevalue');
}else{
value = target.innerHTML;
}
_format(command,value);
e.cancelBubble = true;
fontPicker.style.display = 'none';
}
});
/*******************************************************/
var colorPicker = $.CE('div');
toolbar.appendChild(colorPicker);
colorPicker.className = "colorpicker";
colorPicker.innerHTML = $.colorPickerHtml();
/*******************************************************/
$.addEvent(toolbar['forecolor'],'click',function(){
bind_select_event(this,colorPicker);
});
$.addEvent(toolbar['backcolor'],'click',function(){
bind_select_event(this,colorPicker);
});
/*******************************************************/
$.addEvent(colorPicker,'mouseover',function(){
var e = arguments[0] || window.event,
td = e.srcElement ? e.srcElement : e.target,
nn = td.nodeName.toLowerCase(),
colorView = document.getElementById('color_view'),
colorCode = document.getElementById('color_code');
if( 'td' == nn){
colorView.style.backgroundColor = td.bgColor;
colorCode.innerHTML = td.bgColor;
}
});
/*******************************************************/
$.addEvent(colorPicker,'click',function(){
var e = arguments[0] || window.event,
td = e.srcElement ? e.srcElement : e.target,
nn = td.nodeName.toLowerCase();
if(nn == 'td'){
var cmd = colorPicker.getAttribute("title");
var val = td.bgColor;
_format(cmd,val);
e.cancelBubble = true;
colorPicker.style.display = 'none';
}
});

var _format = function(x,y){//内部私有函数,处理富文本编辑器的格式化命令
iframeDocument.execCommand(x,false,y);
iframe.contentWindow.focus();
}
addSheet('\
#RTE_iframe{width:600px;height:300px;}\
#RTE_toolbar{float:left;width:600px;background:#D5F3F4;}\
#RTE_toolbar .button{display:block;float:left;border:1px solid #CCC;margin-left:5px;\
color:#000;background:#D0E8FC;height:20px;text-align:center;padding:0 10px;white-space: nowrap;}\
#RTE_toolbar select{float:left;height:20px;width:60px;margin-right:5px;}\
#RTE_toolbar .button:hover{color:#fff;border-color:#fff #aaa #aaa #fff;}\
div.fontpicker{display:none;height:150px;overflow:auto;position:absolute;\
border:2px solid #c3c9cf;background:#F1FAFA;}\
div.fontpicker a{display:block;text-decoration:none;color:#000;background:#F1FAFA;padding:2px;}\
div.fontpicker a:hover{color:#999;background:#e3e6e9;}\
div.colorpicker {display:none;position:absolute;width:216px;border:2px solid #c3c9cf;background:#f8f8f8;}\
div.colorpicker table{border-collapse:collapse;margin:0;padding:0;}\
div.colorpicker .cell td{height:12px;width:12px;}\
#color_result{width:216px;}\
#color_view{width:110px;height:25px;}');
}
}

window.onload = function(){
new RichTextEditor({
id:'editor',
textarea_id:'textarea'
});
}

</script>
<title>富文本编辑器</title>
</head>
<body>
<form action="#">
<textarea id="textarea" wrap="on"></textarea>
</form>
</body>
</html>

运行代码

紧接着是查看按钮,这个简单,这里我把封装一下,让它看起来不那么乱。

/********切换回代码界面*************/
var _doHTML = function() {
iframe.style.display = "none";
textarea.style.display = "block";
textarea.value = iframeDocument.body.innerHTML;
textarea.focus();
};
/********切换回富文本编辑器界面*************/
var _doRich = function() {
iframe.style.display = "block";
textarea.style.display = "none";
iframeDocument.body.innerHTML = textarea.value;
iframe.contentWindow.focus();
};
/********切换编辑模式的开关*************/
var switchEditMode = true;
$.addEvent(toolbar['html'], 'click', function(){
if(switchEditMode){
_doHTML();
switchEditMode = false;
}else{
_doRich();
switchEditMode = true;
}
});

但这个不保证我们提交表单时textarea有东西,我们在iframe失去焦点时偷偷转移东西给textarea。这里的问题第二部分已详细提过,这里就不重复了

$.addEvent(iframe.contentWindow,"blur",function(){
textarea.value = iframeDocument.body.innerHTML;
});

“接着下来我们开始讲解复杂插入吧……”正想这样说,一看篇幅,改写成类比预期的费笔墨,今天就先开过头,下次再说。

我们先多添加一个按钮,用于插入表格,点击它将弹出一个层,上面要求我们填写将要生成的表格的参数。

var buttons = {//工具栏的按钮集合
/*********略************/
'table':'插入表格',
'html':'查看'
};

var tableCreator = $.CE('div');
tableCreator.className = 'tablecreator';
toolbar.appendChild(tableCreator);
tableCreator.innerHTML = $.tableHtml();
$.addEvent(toolbar['table'],'click',function(){
bind_select_event(this,tableCreator);
});


<!doctype html>
<html dir="ltr" lang="zh-CN">
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<style type="text/css">
#textarea {width:600px;height:300px;background:#F2F1D7;}
</style>
<script type="text/javascript">
var addSheet = function(){
var doc,cssCode;
if(arguments.length == 1){
doc = document;
cssCode = arguments[0];
}else if(arguments.length == 2){
doc = arguments[0];
cssCode = arguments[1];
}else{
alert("addSheet函数最多接受两个参数!");
}
var headElement = doc.getElementsByTagName("head")[0];
var styleElements = headElement.getElementsByTagName("style");
if(styleElements.length == 0){/*如果不存在style元素则创建*/
if(!+"\v1"){ //ie
doc.createStyleSheet();
}else{
var tempStyleElement = doc.createElement('style');//w3c
tempStyleElement.setAttribute("type", "text/css");
headElement.appendChild(tempStyleElement);
}
}
var styleElement = styleElements[0];
var media = styleElement.getAttribute("media");
if(media != null && !/screen/.test(media.toLowerCase()) ){
styleElement.setAttribute("media","screen");
}
if(!+"\v1"){ //ie
styleElement.styleSheet.cssText += cssCode;
}else if(/a/[-1]=='a'){
styleElement.innerHTML += cssCode;//火狐支持直接innerHTML添加样式表字串
}else{
styleElement.appendChild(doc.createTextNode(cssCode))
}
}
var Class = {
create: function() {
return function() {
this.initialize.apply(this, arguments);
}
}
}
var extend = function(destination, source) {
for (var property in source) {
destination[property] = source[property];
}
return destination;
}

var RichTextEditor = Class.create();//我们的富文本编辑器类
RichTextEditor.prototype = {
initialize:function(options){
this.setOptions(options);
this.drawEditor(this.options.textarea_id);
},
setOptions:function(options){
this.options = { //这里集中设置默认属性
id:'jeditor_'+ new Date().getTime(),
textarea_id:null//用于textarea的ID,也就是我们的必选项
}
extend(this.options, options || {});//这里是用来重写默认属性
},
ID:function(id){return document.getElementById(id) },//getElementById的快捷方式
TN:function(tn){ return document.getElementsByTagName(tn) },//getElementsByTagName的快捷方式
CE:function(s){ return document.createElement(s)},//createElement的快捷方式
fontPickerHtml:function(type,array){
var builder = [];
for(var i = 0,l = array.length;i<l;i++){
builder.push('<a unselectable="on" style="');
if(type == 'fontname'){
builder.push('font-family');
builder.push(':');
builder.push(array[i]); /*呈现一行(一行就是一种字体)*/
builder.push(';" href="javascript:void(0)">');
builder.push(array[i]);
}else if(type == 'fontsize'){
/*呈现一行(一行就是一种字号)*/
builder.push('font-size');
builder.push(':');
builder.push(array[i][1]);
builder.push(';" sizevalue="');
builder.push(array[i][0]);
builder.push('" href="javascript:void(0)">');
builder.push(array[i][2]);
}
builder.push("</a>");
}
return builder.join('');
},
tableHtml: function(){
var _drawInput = function(builder, name, value){
builder.push('<input id="');
builder.push(name);
builder.push('" value="');builder.push(value);builder.push('" unselectable="on"/>');
};
var builder = [];
builder.push('<table>');
// 标题
builder.push('<tr><td colspan="2" style="padding:2px" bgcolor="#D0E8FC">');
builder.push('插入表格');
builder.push('</td></tr>');
// 行数
builder.push('<tr><td>行数</td><td>');
_drawInput(builder, 'rows', 3);
builder.push('</td></tr>');
// 列数
builder.push('<tr><td>列数</td><td>');
_drawInput(builder, 'cols', 5);
builder.push('</td></tr>');
// 宽度
builder.push('<tr><td>宽度</td><td>');
_drawInput(builder, 'width', 300);
builder.push('</td></tr>');
// 提交
builder.push('<tr><td colspan="2" style="padding-top:6px;">');
builder.push('<input type="button" id="rte_submit" value="提交" unselectable="on" onclick="alert(\'尚未完工\')"/>');
builder.push('<input type="button" id="rte_cancel" value="取消" unselectable="on" onclick="alert(\'尚未完工\')"/>');
builder.push('</td></tr>');
builder.push('</table>');
return builder.join('');
},
//用于生成颜色选择器的具体内容
colorPickerHtml : function(){
var _hex = ['FF', 'CC', '99', '66', '33', '00'];
var builder = [];
// 呈现一个颜色格
var _drawCell = function(builder, red, green, blue){
builder.push('<td bgcolor="');
builder.push('#' + red + green + blue);
builder.push('" unselectable="on"></td>');
};
// 呈现一行颜色
var _drawRow = function(builder, red, blue){
builder.push('<tr>');
for (var i = 0; i < 6; ++i) {
_drawCell(builder, red, _hex[i], blue)
}
builder.push('</tr>');
};
// 呈现六个颜色区之一
var _drawTable = function(builder, blue){
builder.push('<table class="cell" unselectable="on">');
for (var i = 0; i < 6; ++i) {
_drawRow(builder, _hex[i], blue)
}
builder.push('</table>');
};
//开始创建
builder.push('<table><tr>');
for (var i = 0; i < 3; ++i) {
builder.push('<td>');
_drawTable(builder, _hex[i]);
builder.push('</td>');
}
builder.push('</tr><tr>');
for (var i = 3; i < 6; ++i) {
builder.push('<td>');
_drawTable(builder, _hex[i])
builder.push('</td>');
}
builder.push('</tr></table>');
builder.push('<table id="color_result"><tr><td id="color_view"></td><td id="color_code"></td></tr></table>');
return builder.join('');
},
addEvent:function(el, type, fn ) {
if(!+"\v1") {
el['e'+type+fn]=fn;
el.attachEvent( 'on'+type, function() {
el['e'+type+fn]();
} );
}else{
el.addEventListener( type, fn, false );
}
},
drawEditor:function(id){
var $ = this,
textarea = this.ID(id),
toolbar = this.CE('div'),
br = this.CE('br'),//用于清除浮动
iframe = this.CE('iframe');
textarea.style.display = "none";
textarea.parentNode.insertBefore(toolbar,textarea);
textarea.parentNode.insertBefore(br,textarea);
textarea.parentNode.insertBefore(iframe,textarea);
br.style.cssText = "clear:both";
toolbar.setAttribute("id","RTE_toolbar");
iframe.setAttribute("id","RTE_iframe");
iframe.frameBorder=0;
var iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
iframeDocument.designMode = "on";
iframeDocument.open();
iframeDocument.write('<html><head><style type="text/css">body{ font-family:arial; font-size:13px;background:#DDF3FF;border:0; }</style></head></html>');
iframeDocument.close();

var buttons = {//工具栏的按钮集合
'removeFormat':'还原',
'bold': '加粗',
'italic': '斜体',
'underline': '下划线',
'strikethrough':'删除线',
'justifyleft': '居左',
'justifycenter': '居中',
'justifyright': '居右',
'indent':'缩进',
'outdent':'悬挂',
'forecolor':'前景色',
'backcolor':'背景色',
'createlink': '超链接',
'insertimage': '插图',
'fontname': '字体',
'fontsize': '字码',
'insertorderedlist':'有序列表',
'insertunorderedlist':'无序列表',
'table':'插入表格',
'html':'查看'
};var fontFamilies = ['宋体','经典中圆简','微软雅黑', '黑体', '楷体', '隶书', '幼圆', 'Arial', 'Arial Narrow', 'Arial Black', 'Comic Sans MS', 'Courier New', 'Georgia', 'New Roman Times', 'Verdana'] var fontSizes= [[1, 'xx-small', '最小'], [2, 'x-small', '特小'], [3, 'small', '小'], [4, 'medium', '中'], [5, 'large', '大'], [6, 'x-large', '特大'], [7, 'xx-large', '最大']];var buttonClone = $.CE("a"),
fragment = document.createDocumentFragment();
buttonClone.className = 'button';
for (var i in buttons){/*添加命令按钮的名字,样式*/
var button = buttonClone.cloneNode("true");
if(i == 'backcolor'){/*特殊处理背景色按钮*/
if (!+"\v1"){
button.setAttribute("title","background")
}else{
button.setAttribute("title","hilitecolor")
}
}
button.setAttribute("title",i);/*把execCommand的命令参数放到title*/
button.innerHTML = buttons[i];
button.setAttribute("unselectable", "on");/*防止焦点转移到点击的元素上,从而保证文本的选中状态*/
toolbar[i] = button; /*★★★★把元素放进一个数组,用于下一个循环绑定事件!★★★★*/
fragment.appendChild(button);
}
toolbar.appendChild(fragment);
$.addEvent(toolbar, 'click', function(){
var e = arguments[0] || window.event,
target = e.srcElement ? e.srcElement : e.target,
command = target.getAttribute("title");
switch (command){
case 'createlink':
case 'insertimage':
var value = prompt('请输入网址:', 'http://');
_format(command,value);
break;
case 'fontname'://这六个特殊处理,不直接执行fontEdit命令!
case 'fontsize':
case 'forecolor':
case 'backcolor':
case 'html':
case 'table':
return;
default://其他执行fontEdit(cmd, null)命令
_format(command,'');
break;
}
});
/*******************************************************/
var fontPicker = $.CE('div');
fontPicker.className = "fontpicker";
toolbar.appendChild(fontPicker);
/*******************************************************/
$.addEvent(toolbar['fontname'], 'click', function(){
fontPicker.innerHTML = $.fontPickerHtml('fontname',fontFamilies);
fontPicker.style.width = "150px";
bind_select_event(this,fontPicker);
});
/*******************************************************/
$.addEvent(toolbar['fontsize'], 'click', function(){
fontPicker.innerHTML = $.fontPickerHtml('fontsize',fontSizes);
fontPicker.style.width = "100px";
bind_select_event(this,fontPicker);
});
/*******************************************************/
var bind_select_event = function(button,picker){//显示或隐藏选择器
button.style.position = 'relative';
var command = button.getAttribute("title");
if('backcolor' == command){
command = !+"\v1" ? 'backcolor':'hilitecolor';
}
picker.setAttribute("title",command);//转移命令
if(picker.style.display == 'none'){
picker.style.display = 'block';
picker.style.left = button.offsetLeft + 'px';
picker.style.top = (button.clientHeight + button.offsetTop)+ 'px';
}else{
picker.style.display='none';
}
}
/*******************************************************/
$.addEvent(fontPicker,'click',function(){
var e = arguments[0] || window.event,
target = e.srcElement ? e.srcElement : e.target,
command = this.getAttribute("title");
var nn = target.nodeName.toLowerCase();
if(nn == 'a'){
var value;
if('fontsize' == command){
value = target.getAttribute('sizevalue');
}else{
value = target.innerHTML;
}
_format(command,value);
e.cancelBubble = true;
fontPicker.style.display = 'none';
}
});
/*******************************************************/
var colorPicker = $.CE('div');
toolbar.appendChild(colorPicker);
colorPicker.className = "colorpicker";
colorPicker.innerHTML = $.colorPickerHtml();
/*******************************************************/
$.addEvent(toolbar['forecolor'],'click',function(){
bind_select_event(this,colorPicker);
});
$.addEvent(toolbar['backcolor'],'click',function(){
bind_select_event(this,colorPicker);
});
/*******************************************************/
$.addEvent(colorPicker,'mouseover',function(){
var e = arguments[0] || window.event,
td = e.srcElement ? e.srcElement : e.target,
nn = td.nodeName.toLowerCase(),
colorView = $.ID('color_view'),
colorCode = $.ID('color_code');
if( 'td' == nn){
colorView.style.backgroundColor = td.bgColor;
colorCode.innerHTML = td.bgColor;
}
});
/*******************************************************/
$.addEvent(colorPicker,'click',function(){
var e = arguments[0] || window.event,
td = e.srcElement ? e.srcElement : e.target,
nn = td.nodeName.toLowerCase();
if(nn == 'td'){
var cmd = colorPicker.getAttribute("title");
var val = td.bgColor;
_format(cmd,val);
e.cancelBubble = true;
colorPicker.style.display = 'none';
}
});

var _format = function(x,y){//内部私有函数,处理富文本编辑器的格式化命令
iframeDocument.execCommand(x,false,y);
iframe.contentWindow.focus();
}/********切换回代码界面*************/ var _doHTML = function() { iframe.style.display = "none"; textarea.style.display = "block"; textarea.value = iframeDocument.body.innerHTML; textarea.focus(); }; /********切换回富文本编辑器界面*************/ var _doRich = function() { iframe.style.display = "block"; textarea.style.display = "none"; iframeDocument.body.innerHTML = textarea.value; iframe.contentWindow.focus(); }; /********切换编辑模式的开关*************/ var switchEditMode = true; $.addEvent(toolbar['html'], 'click', function(){ if(switchEditMode){ _doHTML(); switchEditMode = false; }else{ _doRich(); switchEditMode = true; } });/***********************************************************/$.addEvent(iframe.contentWindow,"blur",function(){ textarea.value = iframeDocument.body.innerHTML; });/*************************************************/var tableCreator = $.CE('div'); tableCreator.className = 'tablecreator'; toolbar.appendChild(tableCreator); tableCreator.innerHTML = $.tableHtml(); $.addEvent(toolbar['table'],'click',function(){ bind_select_event(this,tableCreator); });addSheet('\
#RTE_iframe{width:600px;height:300px;}\
#RTE_toolbar{float:left;width:600px;background:#D5F3F4;}\
#RTE_toolbar .button{display:block;float:left;border:1px solid #CCC;margin-left:5px;\
color:#000;background:#D0E8FC;height:20px;text-align:center;padding:0 10px;white-space: nowrap;}\
#RTE_toolbar select{float:left;height:20px;width:60px;margin-right:5px;}\
#RTE_toolbar .button:hover{color:#fff;border-color:#fff #aaa #aaa #fff;}\
div.fontpicker{display:none;height:150px;overflow:auto;position:absolute;\
border:2px solid #c3c9cf;background:#F1FAFA;}\
div.fontpicker a{display:block;text-decoration:none;color:#000;background:#F1FAFA;padding:2px;}\
div.fontpicker a:hover{color:#999;background:#e3e6e9;}\
div.colorpicker {display:none;position:absolute;width:216px;border:2px solid #c3c9cf;background:#f8f8f8;}\
div.colorpicker table{border-collapse:collapse;margin:0;padding:0;}\
div.colorpicker .cell td{height:12px;width:12px;}\
#color_result{width:216px;}\
#color_view{width:110px;height:25px;}\
div.tablecreator{display:none;width:176px;position:absolute;border:2px solid #c3c9cf;background:#f8f8f8;padding:1px;}\
div.tablecreator table{border:1px solid #69f;line-height:12px;font-size:12px;border-collapse:collapse;width:100%;}\
div.tablecreator td{font-size:12px;color:#777;text-align:center;}\
#rte_submit,#rte_cancel{font-size:12px;color:#777;border:1px solid #777;background-color:#f4f4f4;margin:5px 3px;}\
#rows, #cols, #width{width:80px;height:14px;line-height:12px;font-size:12px;border:1px solid #69f;}');
}
}

window.onload = function(){
new RichTextEditor({
id:'editor',
textarea_id:'textarea'
});
}

</script>
<title>富文本编辑器</title>
</head>
<body>
<form action="#">
<textarea id="textarea" wrap="on"></textarea>
</form>
</body>
</html>

运行代码

最后留个作业,希望各位博友们思考一下如何创建表格,并把插入到编辑光标之前。那么下一部分再见!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: