Ruby's Louvre

每天学习一点点算法

导航

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

先打造好毛坯,添加上各位按钮与下拉选择框,如字体、字号、对齐、斜体、加粗、编号、缩进、背景色、前景色……为了方便我们按制样式,按钮是用span模拟的。

 
<!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">
      #editor{
        float:left; width:380px;background:#D5F3F4;padding-left:10px;
      }
      #editor span.button{
        display:block;float:left;border: 1px solid  #CCC;margin:1px 5px 1px 0px ;color: #000;
        background: #D0E8FC;width:40px;height:20px;text-align:center;
      }
      #editor select{ display:block; float:left;height:20px;width:60px;margin-right:5px;}
      textarea {width:382px;height:100px;}
    </style>
    <title>富文本编辑器</title>
  </head>
  <body>
    <div id="editor">
      <span class="button">加粗</span>
      <span class="button">斜体</span>
      <span class="button">下划</span>
      <span class="button">居中</span>
      <span class="button">居左</span>
      <span class="button">居右</span>
      <span class="button">悬挂</span>
      <span class="button">缩进</span>
      <span class="button">无序</span>
      <span class="button">有序</span>
      <select>
        <option value="SimSun">宋体</option>
        <option value="LiSu">隶书</option>
        <option value="KaiTi_GB2312">楷体</option>
        <option value="YouYuan">幼圆</option>
        <option value="SimHei">黑体</option>
        <option value="Microsoft YaHei">雅黑</option>
        <option value="Comic Sans MS">Comic Sans MS</option>
      </select>
      <select>
        <option value="1">1</option>
        <option value="2">2</option>
        <option value="3">3</option>
        <option value="4">4</option>
        <option value="5">5</option>
      </select>
      <select>
        <option style="color:#000000" value="#000000">■■</option>
        <option style="color:#FF8080" value="#FF8080">■■</option>
        <option style="color:#FFFF00" value="#FFFF00">■■</option>
        <option style="color:#80FF00" value="#80FF00">■■</option>
        <option style="color:#00FFFF" value="#00FFFF">■■</option>
        <option style="color:#0000FF" value="#0000FF">■■</option>
        <option style="color:#FF00FF" value="#FF00FF">■■</option>
      </select>
      <select>
        <option style="color:#000000" value="#000000">■■</option>
        <option style="color:#FF8080" value="#FF8080">■■</option>
        <option style="color:#FFFF00" value="#FFFF00">■■</option>
        <option style="color:#80FF00" value="#80FF00">■■</option>
        <option style="color:#00FFFF" value="#00FFFF">■■</option>
        <option style="color:#0000FF" value="#0000FF">■■</option>
        <option style="color:#FF00FF" value="#FF00FF">■■</option>
      </select>
    </div>
    <br style="clear:both"/>
    <textarea></textarea>
  </body>
</html>

然后我们就可以加入行为层代码了。富文本编辑器用到一个非常关键的脚本document.execCommand( command, showUI, value )。

参数说明
command
类型:String
要执行的命令的名称
showUI
类型:boolean
是否向用户显示命令特定的对话框或消息框。
value
类型:string
要使用该命令分配的值。并非适用于所有命令。
用法说明
AbsolutePosition 设定元素的 position 属性为“absolute”(绝对)。
BackColor 设置或获取当前选中区的背景颜色。
BlockDirLTR 目前尚未支持。
BlockDirRTL 目前尚未支持。
Bold 切换当前选中区的粗体显示与否。
BrowseMode 目前尚未支持。
Copy 将当前选中区复制到剪贴板。
CreateBookmark 创建一个书签锚或获取当前选中区或插入点的书签锚的名称。
CreateLink 在当前选中区上插入超级链接,或显示一个对话框允许用户指定要为当前选中区插入的超级链接的 URL。
Cut 将当前选中区复制到剪贴板并删除之。
Delete 删除当前选中区。
DirLTR 目前尚未支持。
DirRTL 目前尚未支持。
EditMode 目前尚未支持。
FontName 设置或获取当前选中区的字体。
FontSize 设置或获取当前选中区的字体大小。
ForeColor 设置或获取当前选中区的前景(文本)颜色。
FormatBlock 设置当前块格式化标签。
Indent 增加选中文本的缩进。
InlineDirLTR 目前尚未支持。
InlineDirRTL 目前尚未支持。
InsertButton 用按钮控件覆盖当前选中区。
InsertFieldset 用方框覆盖当前选中区。
InsertHorizontalRule 用水平线覆盖当前选中区。
InsertIFrame 用内嵌框架覆盖当前选中区。
InsertImage 用图像覆盖当前选中区。
InsertInputButton 用按钮控件覆盖当前选中区。
InsertInputCheckbox 用复选框控件覆盖当前选中区。
InsertInputFileUpload 用文件上载控件覆盖当前选中区。
InsertInputHidden 插入隐藏控件覆盖当前选中区。
InsertInputImage 用图像控件覆盖当前选中区。
InsertInputPassword 用密码控件覆盖当前选中区。
InsertInputRadio 用单选钮控件覆盖当前选中区。
InsertInputReset 用重置控件覆盖当前选中区。
InsertInputSubmit 用提交控件覆盖当前选中区。
InsertInputText 用文本控件覆盖当前选中区。
InsertMarquee 用空字幕覆盖当前选中区。
InsertOrderedList 切换当前选中区是编号列表还是常规格式化块。
InsertParagraph 用换行覆盖当前选中区。
InsertSelectDropdown 用下拉框控件覆盖当前选中区。
InsertSelectListbox 用列表框控件覆盖当前选中区。
InsertTextArea 用多行文本输入控件覆盖当前选中区。
InsertUnorderedList 切换当前选中区是项目符号列表还是常规格式化块。
Italic 切换当前选中区斜体显示与否。
JustifyCenter 将当前选中区在所在格式化块置中。
JustifyFull 目前尚未支持。
JustifyLeft 将当前选中区所在格式化块左对齐。
JustifyNone 目前尚未支持。
JustifyRight 将当前选中区所在格式化块右对齐。
LiveResize 迫使 MSHTML 编辑器在缩放或移动过程中持续更新元素外观,而不是只在移动或缩放完成后更新。
MultipleSelection 允许当用户按住 Shift 或 Ctrl 键时一次选中多于一个站点可选元素。
Open 打开。
Outdent 减少选中区所在格式化块的缩进。
OverWrite 切换文本状态的插入和覆盖。
Paste 用剪贴板内容覆盖当前选中区。
PlayImage 目前尚未支持。
Print 打开打印对话框以便用户可以打印当前页。
Redo 重做。
Refresh 刷新当前文档。
RemoveFormat 从当前选中区中删除格式化标签。
RemoveParaFormat 目前尚未支持。
SaveAs 将当前 Web 页面保存为文件。
SelectAll 选中整个文档。
SizeToControl 目前尚未支持。
SizeToControlHeight 目前尚未支持。
SizeToControlWidth 目前尚未支持。
Stop 停止。
StopImage 目前尚未支持。
StrikeThrough 目前尚未支持。
Subscript 目前尚未支持。
Superscript 目前尚未支持。
UnBookmark 从当前选中区中删除全部书签。
Underline 切换当前选中区的下划线显示与否。
Undo 撤消。
Unlink 从当前选中区中删除全部超级链接。
Unselect 清除当前选中区的选中状态。
各游览器的支持情况
firefox支持的方法:
decreaseFontSize heading hiliteColor increaseFontSize insertHTML
更多参见这里
ie7支持的方法:
InsertButton InsertInputButton InsertFieldSet InsertIFrame
InsertImage InsertInputCheckbox InsertInputFileUpload InsertInputHidden
InsertInputImage InsertInputPassword InsertInputRadio InsertInputReset
InsertInputSubmit InsertInputText InsertTextArea InsertSelectListbox
InsertSelectDropdown InsertMarquee Unselect SaveAs
更多参见这里这里
同时支持的方法:
cut  paste  bold delete createLink fontName fontSize backColor foreColor formatBlock
indent outdent insertHorizontalRule insertImage insertOrderedList
insertUnorderedList insertParagraph(有差异) italic justifyRight justifyCenter
 justifyLeft undo redo unlink(有差异) Underline SuperScript SelectAll
w3c(html5)建议的方法:
bold createLink delete formatBlock forwardDelete insertImage insertHTML
insertLineBreak insertOrderedList insertOrderedList insertParagraph insertText
italic redo selectAll subscript superscript undo unlink unselect vendorID

我们把各命令的参数放到按钮的title中,并在onclick与onchange时调用它们。

 
<div id="editor">
  <span class="button" title="bold">加粗</span>
  <span class="button" title="italic">斜体</span>
  <span class="button" title="underline">下划</span>
  <span class="button" title="justifycenter">居中</span>
  <span class="button" title="justifyleft">居左</span>
  <span class="button" title="justifyright">居右</span>
  <span class="button" title="outdent">悬挂</span>
  <span class="button" title="indent">缩进</span>
  <span class="button" title="insertunorderedlist">无序</span>
  <span class="button" title="insertorderedlist">有序</span>
  <select title="fontname">
    <!-- 省略 -->
  </select>
  <select title="fontsize">
    <!-- 省略 -->
  </select>
  <select title="forecolor">
    <!-- 省略 -->
  </select>
  <select title="backcolor">
    <!-- 省略 -->
  </select>
</div>
 
  window.onload = function(){
    var editor = document.getElementById("editor");
    var buttons = editor.getElementsByTagName("span");
    var selects = editor.getElementsByTagName("select");
    for(var i = 0,l= buttons.length;i<l;i++){
      buttons[i].onclick = new function(){
        var button=buttons[i];
        return function(){
          document.getElementById("exe").innerHTML =
            "调用了"+button.getAttribute("title")+"命令";
        }
      };
    };
    for(i=0,l=selects.length;i<l;i++){
      selects[i].onchange = new function(){
        var select = selects[i];
        return function(){
          document.getElementById("exe").innerHTML =
            "调用了"+select.getAttribute("title")+"命令,并传入了"+
            select.options[select.selectedIndex].value+"参数!";
        };
      };
    };
  }

好了,那么怎么样编辑文本区(textarea)的文字呢?错,文本区的文字本身就可以编辑,就是怎么也不可能显示其编辑后的效果。我们通常是在div与iframe中编辑。要想在DIV中编辑,设置其contentEditable = "true",若iframe,还可以用designMode="on"。通常人们都是用ifame,这样做当然有它的道理,但一开始,照顾初心者,我们用div实现一下吧。

 
<div id="workaround">

</div>
 
 #workaround {width:382px;height:100px;border:1px solid #0065FD;}

那么怎么获得我们选中的文字?不,这个提法也不对,我们不是对文字直接进行操作,而对能对这些文字进行操作的对象进行操作……实在太拗口了!这就是传说中的Range对象了!

 
    var getRangeObject = function(){
      if(document.implementation.hasFeature("Range", "2.0")){
        return  document.createRange();//W3C
      }else{
        return  document.selection.createRange();//IE
      }
    }

但上面这个函数离我们的目标有些差距,无错,document.selection.createRange.execCommand('bold')就可以把我们选出中的文字加粗了,但火狐那边就不是这回事!这与它们的执行模式有关,当我们选中一段文字时,IE会立即把它们放进Range对象中,但符合W3C标准的游览器不会,它是空的,虽然我们手动加入……手动加入就手动加入吧,自己动手,丰衣足食!

 
    var getRangeObject = function() {
      if (window.getSelection){//W3C
        var range = document.createRange(),
        selection = window.getSelection();
        range.setStart(selection.anchorNode, selection.anchorOffset);
        range.setEnd(selection.focusNode, selection.focusOffset);
        return range;
      }else {   //IE
        return document.selection.createRange();
      }
    }

但是还是失败了,究其原因,标准的Range对象(有关Range的介绍)是没有execCommand()方法。而IE的Range对象,说到底是TextRange对象,两者有很大差别。那怎么办?先放一放它,先实现了IE的再说,以后我们用iframe的document来执行execCommand()便是!

 
  window.onload = function(){
    var editor = document.getElementById("editor");
    var buttons = editor.getElementsByTagName("span");
    var selects = editor.getElementsByTagName("select");
    var workaround = document.getElementById("workaround");
    workaround.contentEditable = "true";
    for(var i = 0,l= buttons.length;i<l;i++){
      buttons[i].onclick = new function(){
        var command=buttons[i].getAttribute("title");
        return function(){
          document.selection.createRange().execCommand(command);//IE only
        }
      };
    };
    for(i=0,l=selects.length;i<l;i++){
      selects[i].onchange = new function(){
        var select = selects[i];
        return function(){
          var command = select.getAttribute("title"),
          value = select.options[select.selectedIndex].value;
          document.selection.createRange().execCommand(command,false,value);//IE only
        };
      };
    };
  }

注意,我们在按扭上面绑定了一个IE私有属性unselectable,并设置为on,这是防止当我们点击按钮时,TextRange对象因为选择对象转移,把原来选择的文本丢失了。其他游览器也有相似的功能,如safari,chrome为onselectstart="return false;" ,火狐为 style="-moz-user-select:none;"。

虽然兼容火狐失败,但作为副产品,搞了这个

下一部分,我们将改用iframe实现富本编辑器,处理众多的兼容问题!

posted on 2009-07-30 19:17  司徒正美  阅读(13333)  评论(25编辑  收藏  举报