码迷,mamicode.com
首页 > 其他好文 > 详细

iframe从光标处插入图片(失去焦点后仍然可以在原位置插入)

时间:2015-02-06 20:13:06      阅读:115      评论:0      收藏:0      [点我收藏+]

标签:

转载请注明: 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的时候,就重新锁定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的封装。

下面是从kindeditor中扒下的代码

  1 function _extend(child, parent, proto) {
  2     if (!proto) {
  3         proto = parent;
  4         parent = null;
  5     }
  6     var childProto;
  7     if (parent) {
  8         var fn = function () {};
  9         fn.prototype = parent.prototype;
 10         childProto = new fn();
 11         _each(proto, function(key, val) {
 12             childProto[key] = val;
 13         });
 14     } else {
 15         childProto = proto;
 16     }
 17     childProto.constructor = child;
 18     child.prototype = childProto;
 19     child.parent = parent ? parent.prototype : null;
 20 }
 21 function _iframeDoc(iframe) {
 22     // iframe = _get(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     if (nodes.length === 0) {
 42         return {node: parent.parentNode, offset: K(parent).index()};
 43     }
 44     var startNode = doc, startPos = 0, cmp = -1;
 45     var testRange = rng.duplicate();
 46     _moveToElementText(testRange, parent);
 47     for (var i = 0, len = nodes.length; i < len; i++) {
 48         var node = nodes[i];
 49         cmp = testRange.compareEndPoints(‘StartToStart‘, pointRange);
 50         if (cmp === 0) {
 51             return {node: node.parentNode, offset: i};
 52         }
 53         if (node.nodeType == 1) {
 54             var nodeRange = rng.duplicate(), dummy, knode = K(node), newNode = node;
 55             if (knode.isControl()) {
 56                 dummy = doc.createElement(‘span‘);
 57                 knode.after(dummy);
 58                 newNode = dummy;
 59                 startPos += knode.text().replace(/\r\n|\n|\r/g, ‘‘).length;
 60             }
 61             _moveToElementText(nodeRange, newNode);
 62             testRange.setEndPoint(‘StartToEnd‘, nodeRange);
 63             if (cmp > 0) {
 64                 startPos += nodeRange.text.replace(/\r\n|\n|\r/g, ‘‘).length;
 65             } else {
 66                 startPos = 0;
 67             }
 68             if (dummy) {
 69                 K(dummy).remove();
 70             }
 71         } else if (node.nodeType == 3) {
 72             testRange.moveStart(‘character‘, node.nodeValue.length);
 73             startPos += node.nodeValue.length;
 74         }
 75         if (cmp < 0) {
 76             startNode = node;
 77         }
 78     }
 79     if (cmp < 0 && startNode.nodeType == 1) {
 80         return {node: parent, offset: K(parent.lastChild).index() + 1};
 81     }
 82     if (cmp > 0) {
 83         while (startNode.nextSibling && startNode.nodeType == 1) {
 84             startNode = startNode.nextSibling;
 85         }
 86     }
 87     testRange = rng.duplicate();
 88     _moveToElementText(testRange, parent);
 89     testRange.setEndPoint(‘StartToEnd‘, pointRange);
 90     startPos -= testRange.text.replace(/\r\n|\n|\r/g, ‘‘).length;
 91     if (cmp > 0 && startNode.nodeType == 3) {
 92         var prevNode = startNode.previousSibling;
 93         while (prevNode && prevNode.nodeType == 3) {
 94             startPos -= prevNode.nodeValue.length;
 95             prevNode = prevNode.previousSibling;
 96         }
 97     }
 98     return {node: startNode, offset: startPos};
 99 }
100 function _getEndRange(node, offset) {
101     var doc = node.ownerDocument || node,
102     range = doc.body.createTextRange();
103     if (doc == node) {
104         range.collapse(true);
105         return range;
106     }
107     if (node.nodeType == 1 && node.childNodes.length > 0) {
108         var children = node.childNodes, isStart, child;
109         if (offset === 0) {
110             child = children[0];
111             isStart = true;
112         } else {
113             child = children[offset - 1];
114             isStart = false;
115         }
116         if (!child) {
117             return range;
118         }
119         if (K(child).name === ‘head‘) {
120             if (offset === 1) {
121                 isStart = true;
122             }
123             if (offset === 2) {
124                 isStart = false;
125             }
126             range.collapse(isStart);
127             return range;
128         }
129         if (child.nodeType == 1) {
130             var kchild = K(child), span;
131             if (kchild.isControl()) {
132                 span = doc.createElement(‘span‘);
133                 if (isStart) {
134                     kchild.before(span);
135                 } else {
136                     kchild.after(span);
137                 }
138                 child = span;
139             }
140             _moveToElementText(range, child);
141             range.collapse(isStart);
142             if (span) {
143                 K(span).remove();
144             }
145             return range;
146         }
147         node = child;
148         offset = isStart ? 0 : child.nodeValue.length;
149     }
150     var dummy = doc.createElement(‘span‘);
151     K(node).before(dummy);
152     _moveToElementText(range, dummy);
153     range.moveStart(‘character‘, offset);
154     K(dummy).remove();
155     return range;
156 }
157 function _toRange(rng) {
158     var doc, range;
159     if (_IERANGE) {
160         if (rng.item) {
161             doc = _getDoc(rng.item(0));
162             range = new KRange(doc);
163             range.selectNode(rng.item(0));
164             return range;
165         }
166         doc = rng.parentElement().ownerDocument;
167         var start = _getStartEnd(rng, true),
168         end = _getStartEnd(rng, false);
169         range = new KRange(doc);
170         range.setStart(start.node, start.offset);
171         range.setEnd(end.node, end.offset);
172         return range;
173     }
174     var startContainer = rng.startContainer;
175     doc = startContainer.ownerDocument || startContainer;
176     range = new KRange(doc);
177     range.setStart(startContainer, rng.startOffset);
178     range.setEnd(rng.endContainer, rng.endOffset);
179     return range;
180 }
181 function KRange(doc) {
182     this.init(doc);
183 }
184 _extend(KRange,{
185     init : function(doc) {
186         var self = this;
187         self.startContainer = doc;
188         self.startOffset = 0;
189         self.endContainer = doc;
190         self.endOffset = 0;
191         self.collapsed = true;
192         self.doc = doc;
193     },
194     setStart : function(node, offset) {
195         var self = this, doc = self.doc;
196         self.startContainer = node;
197         self.startOffset = offset;
198         if (self.endContainer === doc) {
199             self.endContainer = node;
200             self.endOffset = offset;
201         }
202         return _updateCollapsed(this);
203     },
204     setEnd : function(node, offset) {
205         var self = this, doc = self.doc;
206         self.endContainer = node;
207         self.endOffset = offset;
208         if (self.startContainer === doc) {
209             self.startContainer = node;
210             self.startOffset = offset;
211         }
212         return _updateCollapsed(this);
213     },
214     setStartBefore : function(node) {
215         return this.setStart(node.parentNode || this.doc, 0);
216     },
217     setStartAfter : function(node) {
218         return this.setStart(node.parentNode || this.doc, K(node).index() + 1);
219     },
220     setEndBefore : function(node) {
221         return this.setEnd(node.parentNode || this.doc, K(node).index());
222     },
223     setEndAfter : function(node) {
224         return this.setEnd(node.parentNode ||1);
225     },
226     selectNode : function(node) {
227         return this.setStartBefore(node).setEndAfter(node);
228     },
229     selectNodeContents : function(node) {
230         return this.setStart(node, 0).setEnd(node, 0);
231     },
232     collapse : function(toStart) {
233         if (toStart) {
234             return this.setEnd(this.startContainer, this.startOffset);
235         }
236         return this.setStart(this.endContainer, this.endOffset);
237     },
238     cloneRange : function() {
239         return new KRange(this.doc).setStart(this.startContainer, this.startOffset).setEnd(this.endContainer, this.endOffset);
240     },
241     toString : function() {
242         var rng = this.get(), str = _IERANGE ? rng.text : rng.toString();
243         return str.replace(/\r\n|\n|\r/g, ‘‘);
244     },
245     insertNode : function(node) {
246         var self = this,
247         sc = self.startContainer, so = self.startOffset,
248         ec = self.endContainer, eo = self.endOffset,
249         firstChild, lastChild, c, nodeCount = 1;
250         if (sc.nodeType == 1) {
251             c = sc.childNodes[so];
252             if (c) {
253                 sc.insertBefore(node, c);
254                 if (sc === ec) {
255                     eo += nodeCount;
256                 }
257             } else {
258                 sc.appendChild(node);
259             }
260         } else if (sc.nodeType == 3) {
261             if (so === 0) {
262                 sc.parentNode.insertBefore(node, sc);
263                 if (sc.parentNode === ec) {
264                     eo += nodeCount;
265                 }
266             } else if (so >= sc.nodeValue.length) {
267                 if (sc.nextSibling) {
268                     sc.parentNode.insertBefore(node, sc.nextSibling);
269                 } else {
270                     sc.parentNode.appendChild(node);
271                 }
272             } else {
273                 if (so > 0) {
274                     c = sc.splitText(so);
275                 } else {
276                     c = sc;
277                 }
278                 sc.parentNode.insertBefore(node, c);
279                 if (sc === ec) {
280                     ec = c;
281                     eo -= so;
282                 }
283             }
284         }
285         if (firstChild) {
286             self.setStartBefore(firstChild).setEndAfter(lastChild);
287         } else {
288             self.selectNode(node);
289         }
290         return self.setEnd(ec, eo);
291     },
292     isControl : function() {
293         var self = this,
294         sc = self.startContainer, so = self.startOffset,
295         ec = self.endContainer, eo = self.endOffset, rng;
296         return sc.nodeType == 1 && sc === ec && so + 1 === eo && K(sc.childNodes[so]).isControl();
297     },
298     shrink : function() {
299         var self = this, child, collapsed = self.collapsed;
300         while (self.startContainer.nodeType == 1 && (child = self.startContainer.childNodes[self.startOffset]) && child.nodeType == 1 && !K(child).isSingle()) {
301             self.setStart(child, 0);
302         }
303         if (collapsed) {
304             return self.collapse(collapsed);
305         }
306         while (self.endContainer.nodeType == 1 && self.endOffset > 0 && (child = self.endContainer.childNodes[self.endOffset - 1]) && child.nodeType == 1 && !K(child).isSingle()) {
307             self.setEnd(child, child.childNodes.length);
308         }
309         return self;
310     }
311 });
312 function _getDoc(node) {
313     if (!node) {
314         return document;
315     }
316     return node.ownerDocument || node.document || node;
317 }
318 function _getWin(node) {
319     if (!node) {
320         return window;
321     }
322     var doc = _getDoc(node);
323     return doc.parentWindow || doc.defaultView;
324 }
325 function _getSel(doc) {
326     var win = _getWin(doc);
327     return _IERANGE ? doc.selection : win.getSelection();
328 }
329 function _getRng(doc) {
330     var sel = _getSel(doc), rng;
331     try {
332         if (sel.rangeCount > 0) {
333             rng = sel.getRangeAt(0);
334         } else {
335             rng = sel.createRange();
336         }
337     } catch(e) {}
338     if (_IERANGE && (!rng || (!rng.item && rng.parentElement().ownerDocument !== doc))) {
339         return null;
340     }
341     return rng;
342 }
343 function KCmd(range) {
344     this.init(range);
345 }
346 _extend(KCmd,{
347     init : function(range) {
348         var self = this, doc = range.doc;
349         self.doc = doc;
350         self.win = _getWin(doc);
351         self.sel = _getSel(doc);
352         self.range = range;
353     },
354     selection : function(forceReset) {
355         var self = this, doc = self.doc, rng = _getRng(doc);
356         self.sel = _getSel(doc);
357         if (rng) {
358             self.range = _range(rng);
359             return self;
360         }
361         return self;
362     },
363     inserthtml : function(val, quickMode) {
364         var self = this, range = self.range;
365         if (val === ‘‘) {
366             return self;
367         }
368         function insertHtml(range, val) {
369             var doc = range.doc,
370             frag = doc.createDocumentFragment();
371             function parseHTML(htmlStr, fragment) {
372                 var div = document.createElement("div"), reSingleTag = /^<(\w+)\s*\/?>$/;
373                 htmlStr += ‘‘;
374                 if (reSingleTag.test(htmlStr)) {
375                     return [ document.createElement(RegExp.$1) ];
376                 }
377                 var tagWrap = {
378                     option : [ "select" ],
379                     optgroup : [ "select" ],
380                     tbody : [ "table" ],
381                     thead : [ "table" ],
382                     tfoot : [ "table" ],
383                     tr : [ "table", "tbody" ],
384                     td : [ "table", "tbody", "tr" ],
385                     th : [ "table", "thead", "tr" ],
386                     legend : [ "fieldset" ],
387                     caption : [ "table" ],
388                     colgroup : [ "table" ],
389                     col : [ "table", "colgroup" ],
390                     li : [ "ul" ],
391                     link : [ "div" ]
392                 };
393                 for ( var param in tagWrap) {
394                     var tw = tagWrap[param];
395                     switch (param) {
396                         case "option":
397                         tw.pre = ‘<select multiple="multiple">‘;
398                         break;
399                         case "link":
400                         tw.pre = ‘fixbug<div>‘;
401                         break;
402                         default:
403                         tw.pre = "<" + tw.join("><") + ">";
404                     }
405                     tw.post = "</" + tw.reverse().join("></") + ">";
406                 }
407                 var reMultiTag = /<\s*([\w\:]+)/, match = htmlStr.match(reMultiTag), tag = match ? match[1]
408                 .toLowerCase()
409                 : "";
410                 if (match && tagWrap[tag]) {
411                     var wrap = tagWrap[tag];
412                     div.innerHTML = wrap.pre + htmlStr + wrap.post;
413                     n = wrap.length;
414                     while (--n >= 0)
415                         div = div.lastChild;
416                 } else {
417                     div.innerHTML = htmlStr;
418                 }
419                 if (/^\s/.test(htmlStr))
420                     div.insertBefore(document
421                         .createTextNode(htmlStr.match(/^\s*/)[0]),
422                         div.firstChild);
423                 if (fragment) {
424                     var firstChild;
425                     while ((firstChild = div.firstChild)) {
426                         fragment.appendChild(firstChild);
427                     }
428                     return fragment;
429                 }
430                 return div.children;
431             };
432             var oFrag = document.createDocumentFragment();
433             frag.appendChild(parseHTML(val,oFrag));
434             // range.deleteContents();
435             range.insertNode(frag);
436             // range.collapse(false);
437             // self.select(false);
438         }
439         insertHtml(range, val);
440         return self;
441     }
442 });
443 function _range(mixed) {
444     if (!mixed.nodeName) {
445         return mixed.constructor === KRange ? mixed : _toRange(mixed);
446     }
447     return new KRange(mixed);
448 }
449 function _cmd(mixed) {
450     if (mixed.nodeName) {
451         var doc = _getDoc(mixed);
452         mixed = _range(doc).selectNodeContents(doc.body).collapse(false);
453     }
454     return new KCmd(mixed);
455 }

使用的话,

var t=$(‘editor_iframe‘).contentDocument || $(‘editor_iframe‘).contentWindow.document,cmd=_cmd(doc);
bind(t,‘mouseup‘,function(e){
        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的。在contenteditable中是否行的通,我没有试过。

附件

iframe从光标处插入图片(失去焦点后仍然可以在原位置插入)

标签:

原文地址:http://www.cnblogs.com/TheViper/p/4217305.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!