zoukankan      html  css  js  c++  java
  • iframe从光标处插入图片(失去焦点后仍然可以在原位置插入)

    转载请注明: TheViper http://www.cnblogs.com/TheViper 

    改进版:更简单的 编辑器从光标处插入图片(失去焦点后仍然可以在原位置插入)

    为什么会有这个需求?

    当我们需要一个文本输入框(编辑器),它的功能定位介于专门的富文本编辑器和裸(原生)文本框之间。这时,如果用专门富文本编辑器,如kindeditor,ueditor,显的很大材小用,而且这两个的体积都不小,而体积小的富文本编辑器又是针对现代浏览器的。

    贴吧发帖和知乎发问题的编辑器就是典型的这种需求

    问题的出现

    下面是这个问题的呈现,ie8下,知乎编辑器中插入图片

    首先将光标移到已经输入文字的任意位置,然后让光标在编辑器中失去焦点。上传图片,最后看到图片并没有在光标最后在编辑器中的位置。

    如果没有失去焦点,或者浏览器是现代浏览器,则不存在这个问题。

    网上的解决方案

    网上有很多方案,但至少到现在,我没有看到一个成功的。一般问题都出在编辑器失去焦点后,不能“智能”的在光标原位置插入图片。

    最接近的方案就是很简单的win.document.execCommand("insertImage", '',data);,但这个只能针对现代浏览器。

    我的成功解决方案

    先看下效果

    firefox

    ie8

    注意到在kindeditor,ueditor中,都很好的解决了这个问题,那最简单的方法就是从源码中扒出那些代码。ueditor的源码复杂点,多点,就从kindeditor中扒。kindeditor的源码也就5000多行,还没jquery多.

    kindeditor里面的思路是每次mouseup,keyup的时候,就重新锁定selection,range.

    range的startContainer和endContainer一样,startOffset和endOffset也一样,也就是说创建的range实际上一个里面什么都没有的range,没有选中内容的range当然就是光标了。

    self.afterChange(function(e) {
        cmd.selection();
    });
    ........
        afterChange : function(fn) {
            var self = this, doc = self.doc, body = doc.body;
            K(doc).mouseup(fn);
            return self;
        }

    这个就是最精华的地方,其他都没什么好说的,无非就是对selection,range的封装。

    扒下来的代码

      1 define('editor', function() {
      2     function _extend(child, parent, proto) {
      3         if (!proto) {
      4             proto = parent;
      5             parent = null;
      6         }
      7         var childProto;
      8         if (parent) {
      9             var fn = function () {};
     10             fn.prototype = parent.prototype;
     11             childProto = new fn();
     12             _each(proto, function(key, val) {
     13                 childProto[key] = val;
     14             });
     15         } else {
     16             childProto = proto;
     17         }
     18         childProto.constructor = child;
     19         child.prototype = childProto;
     20         child.parent = parent ? parent.prototype : null;
     21     }
     22     function _iframeDoc(iframe) {
     23         return iframe.contentDocument || iframe.contentWindow.document;
     24     }
     25     var _IERANGE = !window.getSelection;
     26     function _updateCollapsed(range) {
     27         range.collapsed = (range.startContainer === range.endContainer && range.startOffset === range.endOffset);
     28         return range;
     29     }
     30     function _moveToElementText(range, el) {
     31         try {
     32             range.moveToElementText(el);
     33         } catch(e) {}
     34     }
     35     function _getStartEnd(rng, isStart) {
     36         var doc = rng.parentElement().ownerDocument,
     37         pointRange = rng.duplicate();
     38         pointRange.collapse(isStart);
     39         var parent = pointRange.parentElement(),
     40         nodes = parent.childNodes;
     41         var startNode = doc, startPos = 0, cmp = -1;
     42         var testRange = rng.duplicate();
     43         _moveToElementText(testRange, parent);
     44         for (var i = 0, len = nodes.length; i < len; i++) {
     45             var node = nodes[i];
     46             cmp = testRange.compareEndPoints('StartToStart', pointRange);
     47             if (cmp === 0) {
     48                 return {node: node.parentNode, offset: i};
     49             }
     50             if (node.nodeType == 3) {
     51                 testRange.moveStart('character', node.nodeValue.length);
     52                 startPos += node.nodeValue.length;
     53             }
     54             if (cmp < 0) {
     55                 startNode = node;
     56             }
     57         }
     58         testRange = rng.duplicate();
     59         _moveToElementText(testRange, parent);
     60         testRange.setEndPoint('StartToEnd', pointRange);
     61         startPos -= testRange.text.replace(/
    |
    |
    /g, '').length;
     62         if (cmp > 0 && startNode.nodeType == 3) {
     63             var prevNode = startNode.previousSibling;
     64             while (prevNode && prevNode.nodeType == 3) {
     65                 startPos -= prevNode.nodeValue.length;
     66                 prevNode = prevNode.previousSibling;
     67             }
     68         }
     69         return {node: startNode, offset: startPos};
     70     }
     71     function _toRange(rng) {
     72         var doc, range;
     73         if (_IERANGE) {
     74             if (rng.item) {
     75                 doc = _getDoc(rng.item(0));
     76                 range = new KRange(doc);
     77                 range.selectNode(rng.item(0));
     78                 return range;
     79             }
     80             doc = rng.parentElement().ownerDocument;
     81             var start = _getStartEnd(rng, true),
     82             end = _getStartEnd(rng, false);
     83             range = new KRange(doc);
     84             range.setStart(start.node, start.offset);
     85             range.setEnd(end.node, end.offset);
     86             return range;
     87         }
     88         var startContainer = rng.startContainer;
     89         doc = startContainer.ownerDocument || startContainer;
     90         range = new KRange(doc);
     91         range.setStart(startContainer, rng.startOffset);
     92         range.setEnd(rng.endContainer, rng.endOffset);
     93         return range;
     94     }
     95     function KRange(doc) {
     96         this.init(doc);
     97     }
     98     _extend(KRange,{
     99         init : function(doc) {
    100             var self = this;
    101             self.startContainer = doc;
    102             self.startOffset = 0;
    103             self.endContainer = doc;
    104             self.endOffset = 0;
    105             self.collapsed = true;
    106             self.doc = doc;
    107         },
    108         setStart : function(node, offset) {
    109             var self = this, doc = self.doc;
    110             self.startContainer = node;
    111             self.startOffset = offset;
    112             if (self.endContainer === doc) {
    113                 self.endContainer = node;
    114                 self.endOffset = offset;
    115             }
    116             return _updateCollapsed(this);
    117         },
    118         setEnd : function(node, offset) {
    119             var self = this, doc = self.doc;
    120             self.endContainer = node;
    121             self.endOffset = offset;
    122             if (self.startContainer === doc) {
    123                 self.startContainer = node;
    124                 self.startOffset = offset;
    125             }
    126             return _updateCollapsed(this);
    127         },
    128         setStartBefore : function(node) {
    129             return this.setStart(node.parentNode || this.doc, 0);
    130         },
    131         setEndAfter : function(node) {
    132             return this.setEnd(node.parentNode ||1);
    133         },
    134         selectNode : function(node) {
    135             return this.setStartBefore(node).setEndAfter(node);
    136         },
    137         selectNodeContents : function(node) {
    138             return this.setStart(node, 0).setEnd(node, 0);
    139         },
    140         collapse : function(toStart) {
    141             if (toStart) {
    142                 return this.setEnd(this.startContainer, this.startOffset);
    143             }
    144             return this.setStart(this.endContainer, this.endOffset);
    145         },
    146         cloneRange : function() {
    147             return new KRange(this.doc).setStart(this.startContainer, this.startOffset).setEnd(this.endContainer, this.endOffset);
    148         },
    149         toString : function() {
    150             var rng = this.get(), str = _IERANGE ? rng.text : rng.toString();
    151             return str.replace(/
    |
    |
    /g, '');
    152         },
    153         insertNode : function(node) {
    154             var self = this,
    155             sc = self.startContainer, so = self.startOffset,
    156             ec = self.endContainer, eo = self.endOffset,
    157             firstChild, lastChild, c, nodeCount = 1;
    158             if (sc.nodeType == 1) {
    159                 c = sc.childNodes[so];
    160                 if (c) {
    161                     sc.insertBefore(node, c);
    162                     if (sc === ec) {
    163                         eo += nodeCount;
    164                     }
    165                 } else {
    166                     sc.appendChild(node);
    167                 }
    168             } else if (sc.nodeType == 3) {
    169                 if (so === 0) {
    170                     sc.parentNode.insertBefore(node, sc);
    171                     if (sc.parentNode === ec) {
    172                         eo += nodeCount;
    173                     }
    174                 } else if (so >= sc.nodeValue.length) {
    175                     if (sc.nextSibling) {
    176                         sc.parentNode.insertBefore(node, sc.nextSibling);
    177                     } else {
    178                         sc.parentNode.appendChild(node);
    179                     }
    180                 } else {
    181                     if (so > 0) {
    182                         c = sc.splitText(so);
    183                     } else {
    184                         c = sc;
    185                     }
    186                     sc.parentNode.insertBefore(node, c);
    187                     if (sc === ec) {
    188                         ec = c;
    189                         eo -= so;
    190                     }
    191                 }
    192             }
    193             if (firstChild) {
    194                 self.setStartBefore(firstChild).setEndAfter(lastChild);
    195             } else {
    196                 self.selectNode(node);
    197             }
    198             return self.setEnd(ec, eo);
    199         }
    200     });
    201     function _getDoc(node) {
    202         if (!node) {
    203             return document;
    204         }
    205         return node.ownerDocument || node.document || node;
    206     }
    207     function _getWin(node) {
    208         if (!node) {
    209             return window;
    210         }
    211         var doc = _getDoc(node);
    212         return doc.parentWindow || doc.defaultView;
    213     }
    214     function _getSel(doc) {
    215         var win = _getWin(doc);
    216         return _IERANGE ? doc.selection : win.getSelection();
    217     }
    218     function _getRng(doc) {
    219         var sel = _getSel(doc), rng;
    220         try {
    221             if (sel.rangeCount > 0) {
    222                 rng = sel.getRangeAt(0);
    223             } else {
    224                 rng = sel.createRange();
    225             }
    226         } catch(e) {}
    227         if (_IERANGE && (!rng || (!rng.item && rng.parentElement().ownerDocument !== doc))) {
    228             return null;
    229         }
    230         return rng;
    231     }
    232     function KCmd(range) {
    233         this.init(range);
    234     }
    235     _extend(KCmd,{
    236         init : function(range) {
    237             var self = this, doc = range.doc;
    238             self.doc = doc;
    239             self.win = _getWin(doc);
    240             self.sel = _getSel(doc);
    241             self.range = range;
    242         },
    243         selection : function(forceReset) {
    244             var self = this, doc = self.doc, rng = _getRng(doc);
    245             self.sel = _getSel(doc);
    246             if (rng) {
    247                 self.range = _range(rng);
    248                 return self;
    249             }
    250             return self;
    251         },
    252         inserthtml : function(val, quickMode) {
    253             var self = this, range = self.range;
    254             if (val === '') {
    255                 return self;
    256             }
    257             function insertHtml(range, val) {
    258                 var doc = range.doc,
    259                 frag = doc.createDocumentFragment();
    260                 function parseHTML(htmlStr, fragment) {
    261                     var div = document.createElement("div"), reSingleTag = /^<(w+)s*/?>$/;
    262                     htmlStr += '';
    263                     if (reSingleTag.test(htmlStr)) {
    264                         return [ document.createElement(RegExp.$1) ];
    265                     }
    266                     var tagWrap = {
    267                         option : [ "select" ],
    268                         optgroup : [ "select" ],
    269                         tbody : [ "table" ],
    270                         thead : [ "table" ],
    271                         tfoot : [ "table" ],
    272                         tr : [ "table", "tbody" ],
    273                         td : [ "table", "tbody", "tr" ],
    274                         th : [ "table", "thead", "tr" ],
    275                         legend : [ "fieldset" ],
    276                         caption : [ "table" ],
    277                         colgroup : [ "table" ],
    278                         col : [ "table", "colgroup" ],
    279                         li : [ "ul" ],
    280                         link : [ "div" ]
    281                     };
    282                     for ( var param in tagWrap) {
    283                         var tw = tagWrap[param];
    284                         switch (param) {
    285                             case "option":
    286                             tw.pre = '<select multiple="multiple">';
    287                             break;
    288                             case "link":
    289                             tw.pre = 'fixbug<div>';
    290                             break;
    291                             default:
    292                             tw.pre = "<" + tw.join("><") + ">";
    293                         }
    294                         tw.post = "</" + tw.reverse().join("></") + ">";
    295                     }
    296                     var reMultiTag = /<s*([w:]+)/, match = htmlStr.match(reMultiTag), tag = match ? match[1]
    297                     .toLowerCase()
    298                     : "";
    299                     if (match && tagWrap[tag]) {
    300                         var wrap = tagWrap[tag];
    301                         div.innerHTML = wrap.pre + htmlStr + wrap.post;
    302                         n = wrap.length;
    303                         while (--n >= 0)
    304                             div = div.lastChild;
    305                     } else {
    306                         div.innerHTML = htmlStr;
    307                     }
    308                     if (/^s/.test(htmlStr))
    309                         div.insertBefore(document
    310                             .createTextNode(htmlStr.match(/^s*/)[0]),
    311                             div.firstChild);
    312                     if (fragment) {
    313                         var firstChild;
    314                         while ((firstChild = div.firstChild)) {
    315                             fragment.appendChild(firstChild);
    316                         }
    317                         return fragment;
    318                     }
    319                     return div.children;
    320                 };
    321                 var oFrag = document.createDocumentFragment();
    322                 frag.appendChild(parseHTML(val,oFrag));
    323                 range.insertNode(frag);
    324             }
    325             insertHtml(range, val);
    326             return self;
    327         }
    328     });
    329     function _range(mixed) {
    330         if (!mixed.nodeName) {
    331             return mixed.constructor === KRange ? mixed : _toRange(mixed);
    332         }
    333         return new KRange(mixed);
    334     }
    335     function _cmd(mixed) {
    336         if (mixed.nodeName) {
    337             var doc = _getDoc(mixed);
    338             mixed = _range(doc).selectNodeContents(doc.body).collapse(false);
    339         }
    340         return new KCmd(mixed);
    341     }
    342     return _cmd;
    343 });

    使用的话,

    var t=$('editor_iframe').contentDocument || $('editor_iframe').contentWindow.document,cmd=_cmd(doc);
    bind(t,'mouseup keyup',function(e){
         if(e.keyCode==13)
    $('editor_iframe').focus();//在ie中如果回车键换行,编辑器会失去焦点,不会在原位置插入 cmd.selection(); }); bind($(
    'editor_image'),'click',function(e){ cmd.inserthtml("<img src='http://localhost/my_editor/1.jpg'>"); });

    _cmd(doc).inserthtml("<img src='http://localhost/my_editor/1.jpg'>");就行了。

    doc=win.document,doc是iframe window的document.

    代码其实很少。

    最后说下,这种方案是基于iframe的,而贴吧,知乎的编辑器都是基于div contenteditable=true的,这个用上面的方法也行的通。

    附件

     

  • 相关阅读:
    关于MySQL数据库优化的部分整理
    PHP跨域form提交
    px、dp和sp,这些单位有什么区别?
    301和302 Http状态有啥区别?
    PHP HTTP请求
    php的http_build_query使用
    nginx ssi 模块
    MongoDB学习笔记(一) MongoDB介绍及安装(摘)
    Django Admin 录入中文错误解决办法
    关于python字符串连接的操作
  • 原文地址:https://www.cnblogs.com/TheViper/p/4217305.html
Copyright © 2011-2022 走看看