liufei88866

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

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

01.   
02.var Class = {
03.    create: function() {
04.      return function() {
05.        this.initialize.apply(this, arguments);
06.      }
07.    }
08.  }
09.  var extend = function(destination, source) {
10.    for (var property in source) {
11.      destination[property] = source[property];
12.    }
13.    return destination;
14.  }
15.  var RichTextEditor =  Class.create();//我们的富文本编辑器类
16.  RichTextEditor.prototype = {
17.    initialize:function(options){
18.      this.setOptions(options);
19.      this.drawEditor(this.options.textarea_id);
20.    },
21.    setOptions:function(options){
22.      this.options = { //这里集中设置默认属性
23.        id:\'jeditor_\'+ new Date().getTime(),
24.        textarea_id:null//用于textarea的ID,也就是我们的必选项
25.      }
26.      extend(this.options, options || {});//这里是用来重写默认属性
27.    },
28.    ID:function(id){return document.getElementById(id) },//getElementById的快捷方式
29.    TN:function(tn){ return document.getElementsByTagName(tn) },//getElementsByTagName的快捷方式
30.    CE:function(s){ return document.createElement(s)},//createElement的快捷方式
31.    drawEditor:function(id){
32.      var textarea = this.ID(id);
33.      textarea.style.display = "none";
34.    }
35.  }

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

01.var buttons = {
02.     \'fontname\': {
03.       \'宋体\':\'SimSun\',
04.       \'隶书\':\'LiSu\',
05.       \'楷体\':\'KaiTi_GB2312\',
06.       \'幼圆\':\'YouYuan\',
07.       \'黑体\':\'SimHei\',
08.       \'雅黑\':\'Microsoft YaHei\',
09.       \'仿宋\':\'FangSong\',
10.       \'Comic Sans MS\':\'Comic Sans MS\'
11.     },
12.     \'fontsize\': {
13.       \'特小\': 1,
14.       \'很小\': 2,
15.       \'小\': 3,
16.       \'中\': 4,
17.       \'大\': 5,
18.       \'很大\': 6,
19.       \'特大\':7
20.     },
21.     \'removeformat\':\'还原\',
22.     \'bold\': \'加粗\',
23.     \'italic\': \'斜体\',
24.     \'underline\': \'下划线\',
25.     \'strikethrough\':\'删除线\',
26.     \'justifyleft\': \'居左\',
27.     \'justifycenter\': \'居中\',
28.     \'justifyright\': \'居右\',
29.     \'indent\':\'缩进\',
30.     \'outdent\':\'悬挂\',
31.     \'forecolor\':\'前景色\',
32.     \'backcolor\':\'背景色\',
33.     \'createlink\': \'超链接\',
34.     \'insertimage\': \'插图\',
35.     \'insertorderedlist\':\'有序列表\',
36.     \'insertunorderedlist\':\'无序列表\',
37.     \'html\':\'查看\'
38.   };

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

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

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

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

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

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

01.this.addEvent(toolbar, \'click\', function(){
02.   var e = arguments[0] || window.event,
03.   target = e.srcElement ? e.srcElement : e.target,
04.   command = target.getAttribute("title");
05.   switch (command){
06.     case \'createlink\':
07.     case \'insertimage\':
08.       var value = prompt(\'请输入网址:\', \'http://\');
09.       _format(command,value);
10.       break;
11.     case \'fontname\'://这几个特殊处理
12.     case \'fontsize\':
13.     case \'forecolor\':
14.     case \'backcolor\':
15.     case \'html\':
16.       return;
17.     default:
18.       _format(command,\'\');
19.       break;
20.   }
21. });
22.   
23. var _format = function(x,y){//内部私有函数,处理富文本编辑器的格式化命令
24.   iframeDocument.execCommand(x,false,y);
25.   iframe.contentWindow.focus();
26. }

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

01.  var fontPicker = $.CE(\'div\');
02.  fontPicker.setAttribute(\'unselectable\', \'on\');
03.  fontPicker.className = "fontpicker";
04.  toolbar.appendChild(fontPicker);//字体选择器与字码选择器都是共用一个虚拟select
05.  $.addEvent(toolbar[\'fontname\'], \'click\', function(){
06.    //根据情况选择载入虚拟select的内容
07.  })
08.  $.addEvent(toolbar[\'fontsize\'], \'click\', function(){
09.    //根据情况选择载入虚拟select的内容
10.  })
11.  var bind_select_event = function(button,picker){
12.    //显示或隐藏文字选择器
13.  }
14.  /************************用于生成文字选择器的内容************************/
15.fontPickerHtml:function(type,array){
16.  var builder = [];
17.  for(var i = 0,l = array.length;i<l;i++){
18.    builder.push(\'<a unselectable="on" style="\');
19.    if(type == \'fontname\'){
20.      builder.push(\'font-family\');
21.      builder.push(\':\\'\');
22.      builder.push(array[i]); /*呈现一行(一行就是一种字体)*/
23.      builder.push(\'\\';" href="javascript:void(0)">\');
24.      builder.push(array[i]);
25.    }else if(type == \'fontsize\'){     
26.      builder.push(\'font-size\'); /*呈现一行(一行就是一种字号)*/
27.      builder.push(\':\');
28.      builder.push(array[i][1]);
29.      builder.push(\';" sizevalue="\');
30.      builder.push(array[i][0]);
31.      builder.push(\'" href="javascript:void(0)">\');//IE的a元素必须有href才有悬浮效果
32.      builder.push(array[i][2]);
33.    }
34.    builder.push("</a>");
35.  }
36.  return builder.join(\'\');
37.}

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

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

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

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

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

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

01./********切换回代码界面*************/
02.var _doHTML = function() {
03.  iframe.style.display = "none";
04.  textarea.style.display = "block";
05.  textarea.value = iframeDocument.body.innerHTML;
06.  textarea.focus();
07.};
08./********切换回富文本编辑器界面*************/
09.var _doRich = function() {
10.  iframe.style.display = "block";
11.  textarea.style.display = "none";
12.  iframeDocument.body.innerHTML = textarea.value;
13.  iframe.contentWindow.focus();
14.};
15./********切换编辑模式的开关*************/
16.var switchEditMode = true;
17.$.addEvent(toolbar[\'html\'], \'click\', function(){
18.  if(switchEditMode){
19.    _doHTML();
20.    switchEditMode = false;
21.  }else{
22.    _doRich();
23.    switchEditMode = true;
24.  }
25.});

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

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

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

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

1.var buttons = {//工具栏的按钮集合
2. /*********略************/
3.   \'table\':\'插入表格\',
4.   \'html\':\'查看\'
5. };
1.var tableCreator = $.CE(\'div\');
2.     tableCreator.className = \'tablecreator\';
3.     toolbar.appendChild(tableCreator);
4.     tableCreator.innerHTML = $.tableHtml();
5.     $.addEvent(toolbar[\'table\'],\'click\',function(){
6.       bind_select_event(this,tableCreator);
7.     });

最后留个作业,希望各位博友们思考一下如何创建表格,并把插入到编辑光标之前。

分类:

技术点:

相关文章:

  • 2021-08-16
  • 2022-02-09
  • 2021-11-25
  • 2021-06-20
  • 2021-08-04
  • 2021-08-08
猜你喜欢
  • 2021-12-10
  • 2022-02-09
  • 2022-02-09
  • 2021-11-22
相关资源
相似解决方案