Ruby's Louvre

每天学习一点点算法

导航

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

这部分我将详尽讲述如何插入复杂的html内容与取得编辑光标的位置,顺带完成插入表情的功能。由于IE与FF这两个闹别扭,这两步的实现都是完全不一样的。不说废话了!开始吧!

上次我们已经为我们的富文本编辑打造了表格创建器,但还没有绑定事件!我现在分析一下,当我们点击表格创建器的提交按扭,它应该会把其面板上输入的参数传到一个程序去处理,让它生成一个完整的表格的html,然后再插入到光标的所在位置,最后隐藏表格创建器;如果是点击取消按钮,则什么也不做就消失了!那个生成html的程序很简单,我们依然把它做成私有方法:

   var _createTable = function(rows, cols, width){
        var builder = [];
        builder.push('<table border="1" width="');
        builder.push(width);
        builder.push('">');
        for(var r = 0; r < rows; r++){
          builder.push('<tr>');
          for(var c = 0; c < cols; c++){
            builder.push('<td> </td>');
          }
          builder.push('</tr>');
        }
        builder.push('</table>');
        return builder.join('');
      };

然后是插入html的方法,我们什么也不处理,看看到底是什么情况。

var _insertHTML = function(html){
       if(!+"\v1"){
        iframeDocument.selection.createRange().pasteHTML(html);       
      }else{
           _format('inserthtml',html);
      }
}

再然后是调用函数,我们把它绑定在onclick事件中!

  $.addEvent(tableCreator, 'click', function(){
        var e = arguments[0] || window.event,
        current = e.srcElement ? e.srcElement : e.target,
        submit = $.ID('rte_submit'),
        cancel = $.ID('rte_cancel'),
        rows = $.ID('rows').value,
        cols = $.ID('cols').value,
        width = $.ID('width').value;
        if(current==cancel) {
          this.style.display = 'none';
          return;
        }else if(current==submit){
          var html = _createTable(rows, cols, width);
          _insertHTML(html);
          this.style.display = "none";
        }
      });

发现IE8依然没有反应,FF的最新版本运行良好。下面是分别给出解决的方法。

IE中是利用bookmark对象。这是一个不透明的对象,我们不可以利用toString()方法取得它的内容。它类似google的“网页快照”,能保存我们选中的文本片段。我们可以利用TextRangeName.getbookmark()生成一个bookmark对象,当我们选中文本时,它会自动存入bookmark中。下面是老外给出的有关bookmark的示范。

接下来就简单了,当文档即将失去焦点时,我们把选项中的文本存在入bookmark对象,当文档再次获得焦点时,我们恢复选中的文本,并在它的后面插入你要插入的内容。

      if(!+"\v1"){
        var bookmark;
        //记录IE的编辑光标
        $.addEvent(iframe,"beforedeactivate",function(){//在文档失去焦点之前
          var range = iframeDocument.selection.createRange();
          bookmark= range.getBookmark();
        });
        //恢复IE的编辑光标
        $.addEvent(iframe,"activate",function(){
          if(bookmark){
            var range = iframeDocument.body.createTextRange();
            range.moveToBookmark(bookmark);
            range.select();
            bookmark = null;
          }
        });
      }

有关beforedeactivate与activate的用法,可以参考这里这里

有人可能会问,为什么不用onblur呢?!它们都是在元素失去焦点时激发!那是因为onbeforedeactive有一个很重要的激活条件——"Fires immediately before the active element is changed from the current object to another object in the parent document."而我们的要求也是当我们的鼠标转移到外面的文档时记录光标位置才有意义!

当焦点离开文档,并不会改变活动元素,也不会派往发onbeforedeactivate事件。我们可以用setAtive()或focus()方法让一个元素成为活动元素,每一个文档最多只有一个活动元素。setActive()不会让文档产生焦点,focus()方法则相反,并让此元素成为活动元素。一个活动元素在失去焦点时会依次发生如下事件:onbeforedeactivate,ondeactivate,和 onblur。 http://snook.ca/archives/mshtml_and_dec/using_getbookma/ http://topic.csdn.net/u/20080222/12/4a8b7832-2298-4ab6-9dc2-779b00ebcce6.html

至于FF,我们可以利用selection对象,它和TextRange对象一样,当我们选中文本时,它会自动存入对象中。我们可以用window.getSelection().getRangeAt(0)或window.getSelection().toString()查看到这些选中的内容。然后插入就不是利用execCommand的inserthtml命令了,而是直接利用Range对象编辑selection的文本就是!

我们首先需要把要插入的内容转化为DocumentFragment。我们可以利用range.createContextualFragment(html) 方法,然后利用insertNode方法把DocumentFragment 加入到range中去,然后再用addRange方法,把range添加到selection中去。

var selection = iframe.contentWindow.getSelection();//取得selection(我们刚才选中的文本)
var range;
 if (selection) {//如果selection不为空,就在selection中创建range对象
     range = selection.getRangeAt(0);
 }else {
     range = iframeDocument.createRange();//否则在iframe的document创建一个
 }
 var oFragment = range.createContextualFragment(html),//把插入内容转变为DocumentFragment
 oLastNode = oFragment.lastChild ;//用于修正编辑光标的位置
 range.insertNode(oFragment) ;
 range.setEndAfter(oLastNode ) ;//把编辑光标放到我们插入内容之后
 range.setStartAfter(oLastNode );
 selection.removeAllRanges();//清除所有选择,要不我们插入的内容与刚才的文本处于选中状态
 selection.addRange(range);//插入内容

接着下来我们实现一下插入表情的功能。这个和上面插入表格没有什么不一样,只不过现在是插入IMG标签而已。关键的地方是如何保存图片的Url。

iconsHtml : function(){
      var builder = [];
      var j = 0;
      var _drawRow = function(builder,i){
        builder.push('<tr>');
        for(var i=0;i<6;i++){
          j++;
          _drawCell(builder,j);
        }
        builder.push('</tr>');
      }
      var _drawCell = function(builder,j){
        var url = 'http://images.cnblogs.com/cnblogs_com/rubylouvre/202906/o_face'+j+'.gif';
        builder.push('<td style="background:url('+url+') center center no-repeat; width:21px;height:21px;"');
        builder.push(' url="'+url+'"> </td>')/*★★★这里偷偷保存url★★★*/
      }
      builder.push('<table border=1>');
      for(var i=0 ;i<6;i++){
        _drawRow(builder,i);
      }
      builder.push('</table>');
      return builder.join('')
    },
      var iconInsertor = $.CE('div');
      toolbar.appendChild(iconInsertor);
      iconInsertor.innerHTML = $.iconsHtml();
      iconInsertor.className = 'emoticons';
      //点击按钮显现表情插入器
      $.addEvent(toolbar['emoticons'],'click',function(){
        bind_select_event(this,iconInsertor);
      });
      $.addEvent(iconInsertor, 'click', function(e){
        var e = arguments[0] || window.event,
        current = e.srcElement ? e.srcElement : e.target,
        td = current.nodeName.toLowerCase();
        if(td == 'td'){
          var k = current.getAttribute("url");
          _insertHTML("<img src='"+k+"'/>");/*★★★取出url★★★*/
          iconInsertor.style.display = "none";
        }
      });

好了,这部分就到此为止。下次再见!也请希望各位厚道点,多多推荐,多多讨论,让我的博客人气旺点!

posted on 2009-08-04 17:00  司徒正美  阅读(6521)  评论(14编辑  收藏  举报