Ruby's Louvre

每天学习一点点算法

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

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

今天是最后一部分,就像马拉松式的结局,既疲惫但又愉悦万分。最后部分我将讲述如何结合CSS Sprites改写我们的工具栏,它实在太难看了,主流的富文本编辑器都是图形化界面。我的美术水平也不甚了了,想更美观的话,就请美工来帮忙吧。然后对部分代码进行重构,让它更加简洁更有效率。

先回顾一下我们原来工具栏的结构:

var buttons = {//工具栏的按钮集合
  'removeFormat':'还原',
  'bold': '加粗',
  'italic': '斜体',
  'underline': '下划线',
  'strikethrough':'删除线',
  'justifyleft': '居左',
  'justifycenter': '居中',
  'justifyright': '居右',
  'indent':'缩进',
  'outdent':'悬挂',
  'forecolor':'前景色',
  'backcolor':'背景色',
  'createlink': '超链接',
  'insertimage': '插图',
  'fontname': '字体',
  'fontsize': '字码',
  'insertorderedlist':'有序列表',
  'insertunorderedlist':'无序列表',
  'table':'插入表格',
  'emoticons':'插入表情',
  'html':'查看'
};

这是一个非常简单的对象,它左边的键被我们当作execCommad函数的第一个参数放于按钮的title中,右边的值就是我们按钮上的文字。当我们把它做成图形化界面后,按钮上的文字就没有意义,或许把它放到title是不是更合适呢?!但那样做的话,title中的命令怎么办?我们得把它们移到另一个属性中。a元素(我们的按钮是用a元素做的)可用的属性不多,我们可以把它们放到一个私有的属性中去,如"command"。作为连带反应,onclick事件都要改写:

        $.addEvent(toolbar, 'click', function(){
        var e = arguments[0] || window.event,
        target = e.srcElement ? e.srcElement : e.target,
        command = target.getAttribute("command");
        switch (command){
/***************略**************/

现在讨论一下如何把工具栏做成图形化了,我们需要一张图片,如果你不是利用CSS Sprites技术,就要准备许多图片,有多少个按钮就有多少张。CSS Sprites技术特别花费人的耐心,要你精确地确定每个按钮使用图片的那个部分。具体来说按钮使用的部分的左上角到图片的左上角的水平距离与垂直距离,还有使用部分的长与高,以前我们都是让按钮自适应按钮文字的长与宽,这次可不是回事。换言之,我们需要为每个按钮准备多4个参数!大家准备好尺子来量度吧!

var buttons = {//工具栏的按钮集合
      'fontname':['字体',-120,-40,86,20],
      'fontsize':['文字大小',-220,-40,86,20],
      'removeformat':['还原',-580,0,20,20],
      'bold':[ '粗体',0,0,20,20],
      'italic':[ '斜体',-60,0,20,20],
      'underline': ['下划线',-140,0,20,20],
      'strikethrough':['删除线',-120,0,20,20],
      'justifyleft': ['居左', -460,0,20,20],
      'justifycenter':[ '居中',-420,0,20,20],
      'justifyright':['居右',-480,0,20,20],
      'justifyfull':['两端对齐',-440,0,20,20],
      'indent':['缩进',-400,0,20,20],
      'outdent':['悬挂',-540,0,20,20],
      'forecolor':['前景色',-720,0,20,20],
      'backcolor':['背景色',-760,0,20,20],
      'createlink':['超级连接',-500,0,20,20],
      'insertimage':['插入图片',-380,0,20,20],
      'insertorderedlist':['有序列表',-80,0,20,20],
      'insertunorderedlist':['无序列表',-20,0,20,20],
      'html':['查看',-260,0,20,20],
      'table':['表格',-580,-20,20,20],
      'emoticons':['表情',-60,-20,20,20]
};

这样,我们把原来对象的值改成一个数组,第一个是title,第二个是backgroundPositionX,第三个是backgroundPositionY,第四个是长,最后一个是宽。这是我们准备使用的图片,里面许多icon没有用到,你们可以自己扩展富文本的功能……

图片

那么怎样遍历生成它呢!只需一个简单的循环:

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.style.cssText = "background-position: "+buttons[i][1]+"px "+buttons[i][2]+"px; width: "+buttons[i][3]+"px;height: "+buttons[i][4]+"px;"
  button.setAttribute("title",buttons[i][0]);
  button.setAttribute("command",i);/*把execCommand的命令参数放到自定义属性command中*/
  button.setAttribute("unselectable", "on");/*防止焦点转移到点击的元素上,从而保证文本的选中状态*/
  toolbar[i] = button;   /*★★★★把元素放进一个数组,用于绑定事件!★★★★*/
  fragment.appendChild(button);
}
toolbar.appendChild(fragment);

你们可以自己先做,做不出再参考我后面放出的代码。

基本到这里它就似模似样了,可以放到项目中使用。但它还有一个不如意的地方,就是我们点击背景色按钮,弹出一个面板,然后再点击icon按钮,它又弹出一个面板。通常富文本编辑器都只允许一个弹出层出现,结合我们的代码,发觉每点击一个这样有弹出层的按钮,它们都产生多一个DIV,由此产生许多冗余代码。我们为何不让它们共用一个弹出层呢?!每次点击时动态载入内容就是!

var popup = $.CE('div');
toolbar.appendChild(popup);
 
/******************************************************************/
$.addEvent(toolbar['fontname'], 'click', function(){
  popup.innerHTML = $.fontPickerHtml('fontname',fontFamilies);
  bind_select_event(this,popup,"fontpicker");
});
/******************************************************************/
$.addEvent(toolbar['fontsize'], 'click', function(){
  popup.innerHTML = $.fontPickerHtml('fontsize',fontSizes);
  bind_select_event(this,popup,"fontpicker");
});
/******************************************************************/
$.addEvent(toolbar['emoticons'],'click',function(){
  popup.innerHTML = $.iconsHtml();
  bind_select_event(this,popup, 'iconinsertor');
});
 
$.addEvent(toolbar['table'],'click',function(){
  popup.innerHTML =  $.tableHtml();
  bind_select_event(this,popup, 'tablecreator');
});
 
/******************************************************************/
$.addEvent(toolbar['forecolor'],'click',function(){
  popup.innerHTML = $.colorPickerHtml();
  bind_select_event(this,popup,"colorpicker");
});
$.addEvent(toolbar['backcolor'],'click',function(){
  popup.innerHTML = $.colorPickerHtml();
  bind_select_event(this,popup,"colorpicker");
});

既然弹出层都合并了,那么多对弹出层的onclick事件也可以合并了。

$.addEvent(popup,'click',function(){
   var e = arguments[0] || window.event,
   element = e.srcElement ? e.srcElement : e.target,
   command = this.getAttribute("title"),
   id = this.getAttribute("id"),
   tag = element.nodeName.toLowerCase();
   switch (id){
     case "fontpicker":
       if(tag == 'a'){
      /*************略*****************/
       break;
     case "colorpicker":
      /*************略*****************/
       break;
     case "tablecreator":
      /*************略*****************/
       break;
     case "iconinsertor":
     /*************略*****************/
       break;
   }
 });

最后是例子与下载附件

附件下载

如果您觉得此文有帮助,可以打赏点钱给我支付宝1669866773@qq.com ,或扫描二维码

posted on   司徒正美  阅读(5060)  评论(20编辑  收藏  举报

编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
点击右上角即可分享
微信分享提示