转载请注明:TheViperhttp://www.cnblogs.com/TheViper
改进版:更简单的 编辑器从光标处插入图片(失去焦点后仍然可以在原位置插入)
为什么会有这个需求?
当我们需要一个文本输入框(编辑器),它的功能定位介于专门的富文本编辑器和裸(原生)文本框之间。这时,如果用专门富文本编辑器,如kindeditor,ueditor,显的很大材小用,而且这两个的体积都不小,而体积小的富文本编辑器又是针对现代浏览器的。
贴吧发帖和知乎发问题的编辑器就是典型的这种需求
问题的出现
下面是这个问题的呈现,ie8下,知乎编辑器中插入图片
首先将光标移到已经输入文字的任意位置,然后让光标在编辑器中失去焦点。上传图片,最后看到图片并没有在光标最后在编辑器中的位置。
如果没有失去焦点,或者浏览器是现代浏览器,则不存在这个问题。
网上的解决方案
网上有很多方案,但至少到现在,我没有看到一个成功的。一般问题都出在编辑器失去焦点后,不能“智能”的在光标原位置插入图片。
最接近的方案就是很简单的win.document.execCommand("insertImage", '',data);,但这个只能针对现代浏览器。
我的成功解决方案
先看下效果
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(//r/n|/n|/r/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(//r/n|/n|/r/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
新闻热点
疑难解答