zoukankan      html  css  js  c++  java
  • TextRange之插入表情

    SNS类或是微博类的产品一般都有一个功能:插入表情,如下所示:

    image

    重点:兼容IE与其它主流的浏览器,根据上一次选择的选区范围进行操作。

    自己写了一个TextRange(参考了网上的一些例子和代码)

    /**
     * @author Meteoric_cry
     */
    
    /**
     * 文本选区操作类
     */
    var TextRange = function() {
    	var inner;
    	
    	return inner = {
    		/**
    		 * 获取选区
    		 * 
    		 * @param {HTMLElement} oElement input或是textarea对象
    		 * @return [start, end] 返回input或textarea选区的开始与结束的索引值
    		 */
    		getRange : function(oElement) {
    			var start = 0, end = 0;
    			
    			if(!document.selection) {//not IE
    				start = oElement.selectionStart;
    				end = oElement.selectionEnd;
    			} else if(document.selection) {
    				var range = document.selection.createRange(),
    					range_all = document.body.createTextRange(),
    					i = 0;
    				
    				
    				range_all.moveToElementText(oElement);
    				/**
    				 * 两个range:一个是已经选择的text(range),一个是整个textarea(range_all)
    				 * 
    				 * compareEndPoints比较两个端点,range_all的起点比range更向左(allIndex - index < 0),则range_all需要向右移动
    				 */
    				for(; range_all.compareEndPoints("StartToStart", range) < 0; start++) {
    					range_all.moveStart('character', 1);
    				}
    				
    				for(; i<start; i++) {
    					if(oElement.value.charAt(i) == "\n") {
    						start++;
    					}
    				}
    				
    				range_all = document.body.createTextRange();
    				range_all.moveToElementText(oElement);
    				
    				for(; range_all.compareEndPoints('StartToEnd', range) < 0; end++) {
    					range_all.moveStart('character', 1);
    				}
    				
    				for(i=0; i <= end; i++) {
    					if(oElement.value.charAt(i) == "\n") {
    						end++;
    					}
    				}
    			}
    			
    			return [start, end];
    		},	
    		/**
    		 * 获取选区的起始位置
    		 * 
    		 * @param {HTMLElement} oElement input或是textarea对象
    		 * @return DOM对象选区的起始位置
    		 */
    		selectionStart : function(oElement) {
    			return inner.getRange(oElement)[0];
    		},
    		/**
    		 * 获取选取前的内容
    		 * 
    		 * @param {HTMLElement} oElement input或是textarea对象
    		 * @return 返回DOM对象选区开始前的文本内容
    		 */
    		selectionBefore : function(oElement) {
    			 return oElement.value.slice(0, inner.getRange(oElement)[0]);
    		},
    		/**
    		 * 设置DOM的选区
    		 * 
    		 * @param {HTMLElement} oElement input或是textarea对象
    		 * @param {Number} start 被设置的选区的起始位置 
    		 * @param {Number} end	被设置的选区的结束位置
    		 */
    		selectText : function(oElement, start, end) {
    			oElement.focus();
    			
    	        if (!document.selection) {
    	            oElement.setSelectionRange(start, end);
    	            return ;
    	        }
    	        var range = oElement.createTextRange();
    	        range.collapse(1);
    	        range.moveStart("character", start);
    	        range.moveEnd("character", end - start);
    	        range.select();
    		},
    		/**
    		 * 插入文本内容
    		 * 
    		 * @param {HTMLElement} oElement
    		 * @param {String} sInsertText
    		 * @param {Number} nStart
    		 * @param {Number} nLen
    		 */
    		insertText : function(oElement, sInsertText, nStart, nLen) {
    			oElement.focus();
    	        nLen = nLen || 0;
    			
    	        if (!document.selection) {
    	            var text = oElement.value,
    	                start = nStart - nLen,
    	                end = start + sInsertText.length;
    				
    	            oElement.value = text.slice(0, start) + sInsertText + text.slice(nStart, text.length);
    	            it.selectText(oElement, end, end);
    	            return ;
    	        }
    	        var c = document.selection.createRange();
    	        c.moveStart("character", -nLen);
    	        c.text = sInsertText;
    		},
    		/**
    		 * 获取光标的位置
    		 * 
    		 * @param {HTMLElement} oElement
    		 * @return cursorPos
    		 */
    		getCursorPos : function(oElement) {
    			var cursorPos = 0;
    			
    	        if (window.ActiveXObject) {
    	            oElement.focus();
    	            var range = document.selection.createRange(),
    					stored_range = range.duplicate();
    	           	
    	            stored_range.moveToElementText(oElement);
    	            stored_range.setEndPoint("EndToEnd", range);
    	            oElement.selectionStart = stored_range.text.length - range.text.length;
    	            oElement.selectionEnd = oElement.selectionStart + range.text.length;
    	            cursorPos = oElement.selectionStart;
    	        } else {
    	            if (oElement.selectionStart || oElement.selectionStart == "0") {
    	                cursorPos = oElement.selectionStart;
    	            }
    	        }
    			
    	        return cursorPos;
    		},
    		/**
    		 * 获取选区内的文本内容
    		 * 
    		 * @param {HTMLElement} oElement
    		 * @return selectedText 选区的文本内容
    		 */
    		getSelectedText : function(oElement) {
    			var selectedText = "";
    			
    	        var getSelection = function (e) {
    	            if (e.selectionStart != undefined && e.selectionEnd != undefined) {
    	                return e.value.slice(e.selectionStart, e.selectionEnd);
    	            } else {
    	                return "";
    	            }
    	        };
    			
    	        if (window.getSelection) {
    	            selectedText = getSelection(oElement)
    	        } else {
    	            selectedText = document.selection.createRange().text;
    	        }
    	        return selectedText;
    		},
    		/**
    		 * 设置光标的位置,默认不选区
    		 * 
    		 * @param {HTMLElement} oElement
    		 * @param {Number} pos
    		 * @param {Number} coverlen
    		 */
    		setCursor : function(oElement, pos, coverlen) {
    			pos = pos ? pos : oElement.value.length;
    			coverlen = coverlen ? coverlen : 0;
    	        oElement.focus();
    			
    	        if (oElement.createTextRange) {
    	            var range = oElement.createTextRange();
    	            range.move("character", pos);
    	            range.moveEnd("character", coverlen);
    	            range.select();
    	        } else {
    	            oElement.setSelectionRange(pos, pos + coverlen);
    	        }
    		},
    		/**
    		 * 插入内容后光标的位置保持不变
    		 * 
    		 * @param {HTMLElement} oElement  
    		 * @param {String} str
    		 * @param {Object} pars
    		 */
    		unCoverInsertText : function(oElement, str, pars) {
    			pars = pars ? pars : {};
    	        pars.start =  pars.start ? pars.start * 1 : 0;
    	        pars.end = pars.end ? pars.end * 1 : oElement.value.length;
    			
    	        var text = oElement.value,
    	            fstr = text.slice(0, pars.start),
    	            lstr = text.slice(pars.end, text == "" ? 0 : text.length);
    			
    			oElement.value = fstr + str + lstr;
    			
    	        inner.setCursor(oElement, pars.start + (str ? str.length : 0));
    		}
    	}
    }();

    先写个例子测试一下TextRange里面的方法:

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>选区的测试</title>
    <script type="text/javascript" src="textRange.js"></script>
    </head>
    <body>
    
    <table cellpadding="0" cellspacing="0" style="border:1px solid #406c99;">
    	<tbody>
    		<tr>
    			<td>start:<input type="text" id="start" /></td>
    			<td>end:<input type="text" id="end" /></td>
    		</tr>
    		<tr>
    			<td colspan="2">
    				<textarea id="txt" style="400px; height:100px;"  onkeyup="savePos(this);"  onmouseup="savePos(this);" onfocus="savePos(this);"></textarea>
    			</td>
    		</tr>
    		<tr>
    			<td><input type="text" id="insTxt" /></td>
    			<td><input type="button" onclick="addText();" value="addText" /></td>
    		</tr>
    	</tbody>
    </table>
    
    
    <div>
    	<button onclick="setSelection();">选中选区</button>
    	<button onclick="setCursorPos();">设置光标位置</button>
    </div>
    
    <div style="border:1px solid #999; 800px; margin-top:30px;">
    	选区之前的内容:<input type="text" id="beforeTxt"	style="600px;"/>
    </div>
    
    <script type="text/javascript">
    	function $(id) {
    		return typeof id === "string" ? document.getElementById(id) : id;
    	}
    	
    	//添加内容
    	function addText() {
    		var elem = $("txt");
    		var range = elem.getAttribute('range') ? elem.getAttribute('range').split("|") : [0, 0];
    		var str_1 = elem.value.slice(0, range[0]);
    		var str_2 = elem.value.slice(range[1]);
    		
    		elem.value = str_1 + $("insTxt").value + str_2;
    	}
    	
    	//保存选区
    	function savePos(elem) {
    		var range = TextRange.getRange(elem);
    		
    		$("start").value = range[0];
    		$("end").value = range[1];
    		
    		var rangeBeforeTxt = TextRange.selectionBefore(elem);
    		$("beforeTxt").value = rangeBeforeTxt.replace(/\n/g, '--');
    		
    		elem.setAttribute("range", range.join("|"));
    	}
    	
    	//设置选区
    	function setSelection() {
    		var start = $("start").value,
    			end = $("end").value;
    			
    		TextRange.selectText($("txt"), start, end);
    		
    		TextRange.unCoverInsertText($("txt"), '#===#', {
                start: start,
                end: end
            })
    	}
    	
    	function setCursorPos() {
    		TextRange.setCursor($("txt"), 3, 5);//索引值、长度
    	}
    </script>
    
    </body>
    </html>

    博客园插入代码好像有问题,复制到本地运行测试一下就行了 :)

    下面就是插入表情的示例代码了:

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>UBB表情的插入</title>
    <script type="text/javascript" src="textRange.js"></script>
    <style type="text/css">
    	* {margin:0; padding:0;}
    	body {font:12px/1.3 Arial, Helvetica, sans-serif; background-color:#FFFFFF;}
    	ul, li {list-style:none}
    	img {border:none}
    	
    	.container {margin:30px;}
    	#txtDemo {475px; height:110px; word-break:break-all; word-wrap:break-word;}
    	#facePanel {border-1px 2px 2px 1px; border-color: #CCCCCC; border-style:solid; -webkit-border-radius: 6px; -moz-border-radius:6px; padding:6px; 330px; margin-top:5px; display:none;}
    	#facePanel li{float:left; 28px; height:28px; padding:0 5px 5px 0;}
    	#facePanel li a {display:block; text-decoration:none; border:1px dashed #DDD; 26px; height:26px; line-height:26px; overflow:hidden; text-align:center;}
    	#facePanel li a:hover {border-color:#999;}
    	
    	#facePanel li a span {display:inline-block;}
    	#facePanel li a img {vertical-align:middle;}
    	
    	#facePanel ul {zoom:1;}
    	#facePanel ul:after{ content:"\0020"; display:block; height:0; line-height:0; clear:both; visibility:hidden;}
    </style>
    </head>
    <body>
    	
    <div class="container">
    	<div>
    		<textarea id="txtDemo"></textarea>
    	</div>
    	<button onclick="showFace(event);">插入表情</button>
    	<div id="facePanel">sadf</div>
    </div>
    
    <script type="text/javascript">
    	function $(id) {
    		return typeof id === 'string' ? document.getElementById(id) : id;
    	}
    	
    	function addEvent(el, type, handler) {
    		if(el.attachEvent) {
    			el.attachEvent("on" + type, handler);
    		} else if(el.addEventListener) {
    			el.addEventListener(type, handler, false);
    		}
    	}
    	
    	function removeEvent(el, type, handler) {
    		if(el.detachEvent) {
    			el.detachEvent('on' + type, handler);
    		} else if(el.removeEventListener) {
    			el.removeEventListener(type, handler, false);
    		}
    	}
    	
    	var faceInited = false;
    	
    	//显示表情
    	function showFace(ev) {
    		if(!faceInited) {
    			initFace();
    		}
    		
    		var facePanel= $("facePanel");
    		
    		facePanel.style.display = "block";
    		addEvent(document.body, 'click', hideFace);
    		addEvent(facePanel, 'click', cancelEventBubble);
    		
    		cancelEventBubble(ev);
    	}
    	
    	//隐藏表情
    	function hideFace() {
    		$("facePanel").style.display = "none";
    		removeEvent(document.body, 'click', hideFace);
    		removeEvent($("facePanel"), 'click', cancelEventBubble);
    	}
    	
    	//插入表情
    	function insertFace(elem) {
    		var txtElem = $("txtDemo"),
    			range = txtElem.getAttribute("range") ? txtElem.getAttribute("range").split("|") : [0, 0];
    		
    		var str_1 = txtElem.value.slice(0, range[0]);
    		var str_2 = txtElem.value.slice(range[1]);
    		
    		txtElem.value = str_1 + elem.getAttribute("value") + str_2;
    		
    		if(!document.selection) {
    			txtElem.selectionStart = txtElem.value.length;
    			txtElem.selectionEnd = txtElem.value.length;
    		} else {
    			var range = txtElem.createTextRange();
    	        range.collapse(1);
    	        range.moveStart("character", txtElem.value.length);
    	        range.moveEnd("character", txtElem.value.length);
    	        range.select();
    		}
    		
    		txtElem.focus();
    		
    		hideFace();
    	}
    	
    	//取消事件冒泡
    	function cancelEventBubble(ev) {
    		ev = ev || window.event;
    		
    		if(ev.stopPropagation) {
    			ev.stopPropagation();
    		} else if(!ev.cancelBubble) {
    			ev.cancelBubble = true;
    		}
    	}
    	
    	//记录textarea的选区的start&end
    	function savePos() {
    		var elem = $("txtDemo"),
    			range = getRange(elem);//获取选区
    		
    		elem.setAttribute("range", range.join("|"));
    		
    		document.title = "start:" + range[0] + "--" + "end:" + range[1];
    		
    		cancelEventBubble(arguments[0] || window.event);
    	}
    	
    	!(function() {
    		var txtElem = $("txtDemo");
    		
    		addEvent(txtElem, 'focus', savePos);
    		addEvent(txtElem, 'mouseup', savePos);
    		addEvent(txtElem, 'keyup', savePos);
    		addEvent(txtElem, 'mousemove', savePos);//Chrome 在选中文本域内的文字时,不能触发mouseup事件,导致range依旧为最近一次的range
    	})();
    	
    	//初始化表情
    	function initFace() {
    		var faces = [{"icon":"\u8db3\u7403","value":"[\u8db3\u7403]","src":"basic\/football.gif"},{"icon":"\u54e8\u5b50","value":"[\u54e8\u5b50]","src":"basic\/shao.gif"},{"icon":"\u7ea2\u724c","value":"[\u7ea2\u724c]","src":"basic\/redcard.gif"},{"icon":"\u9ec4\u724c","value":"[\u9ec4\u724c]","src":"basic\/yellowcard.gif"},{"icon":"\u54c8\u54c8","value":"[\u54c8\u54c8]","src":"basic\/laugh.gif"},{"icon":"\u5475\u5475","value":"[\u5475\u5475]","src":"basic\/smile.gif"},{"icon":"\u6cea","value":"[\u6cea]","src":"basic\/cry.gif"},{"icon":"\u6c57","value":"[\u6c57]","src":"basic\/sweat.gif"},{"icon":"\u7231\u4f60","value":"[\u7231\u4f60]","src":"basic\/love.gif"},{"icon":"\u563b\u563b","value":"[\u563b\u563b]","src":"basic\/tooth.gif"},{"icon":"\u54fc","value":"[\u54fc]","src":"basic\/hate.gif"},{"icon":"\u5fc3","value":"[\u5fc3]","src":"basic\/heart.gif"},{"icon":"\u6655","value":"[\u6655]","src":"basic\/dizzy.gif"},{"icon":"\u6012","value":"[\u6012]","src":"basic\/angry.gif"},{"icon":"\u86cb\u7cd5","value":"[\u86cb\u7cd5]","src":"basic\/cake.gif"},{"icon":"\u82b1","value":"[\u82b1]","src":"basic\/flower.gif"},{"icon":"\u6293\u72c2","value":"[\u6293\u72c2]","src":"basic\/crazy.gif"},{"icon":"\u56f0","value":"[\u56f0]","src":"basic\/sleepy.gif"},{"icon":"\u5e72\u676f","value":"[\u5e72\u676f]","src":"basic\/cheer.gif"},{"icon":"\u592a\u9633","value":"[\u592a\u9633]","src":"basic\/sun.gif"},{"icon":"\u4e0b\u96e8","value":"[\u4e0b\u96e8]","src":"basic\/rain.gif"},{"icon":"\u4f24\u5fc3","value":"[\u4f24\u5fc3]","src":"basic\/sad.gif"},{"icon":"\u6708\u4eae","value":"[\u6708\u4eae]","src":"basic\/moon.gif"},{"icon":"\u732a\u5934","value":"[\u732a\u5934]","src":"basic\/pig.gif"},{"icon":"\u8721\u70db","value":"[\u8721\u70db]","src":"basic\/candle.gif"}];
    		var imgURI = "http://timg.sjs.sinajs.cn/miniblog2style/images/common/face/";
    		var tempArr = [];
    		tempArr.push('<ul>');
    		
    		for(var i=0, len = faces.length; i<len; i++) {
    			tempArr.push([
    				'<li><a href="javascript:;" hideFocus="true" onclick="insertFace(this);return false;" value="' + faces[i].value + '" title="' + faces[i].icon + '"><img src="' + imgURI + faces[i].src + '" alt="' + faces[i].icon + '" /><span>&nbsp;</span></a></li>',
    			].join(""));
    		}
    		
    		tempArr.push('</ul>');
    		
    		$("facePanel").innerHTML = tempArr.join("");
    	}
    	
    	//获取选区
    	function getRange(elem) {
    		var start = 0, end = 0;
    			
    		if(!document.selection) {
    			start = elem.selectionStart;
    			end = elem.selectionEnd;
    		} else if(document.selection) {
    			var range = document.selection.createRange(),
    				range_all = document.body.createTextRange(),
    				i = 0;
    				
    			range_all.moveToElementText(elem);
    			
    			for(; range_all.compareEndPoints("StartToStart", range) < 0; start++) {
    				range_all.moveStart('character', 1);
    			}
    			
    			for(; i<start; i++) {
    				if(elem.value.charAt(i) == "\n") {
    					start++;
    				}
    			}
    			
    			range_all = document.body.createTextRange();
    			range_all.moveToElementText(elem);
    			
    			for(; range_all.compareEndPoints('StartToEnd', range) < 0; end++) {
    				range_all.moveStart('character', 1);
    			}
    			
    			for(i=0; i <= end; i++) {
    				if(elem.value.charAt(i) == "\n") {
    					end++;
    				}
    			}
    		}
    		
    		return [start, end];
    	}
    </script>
    
    </body>
    </html>
  • 相关阅读:
    USACO6.4-The Primes
    ZOJ2112--Dynamic Rankings (动态区间第k大)
    Havel定理
    HDU5107---K-short Problem (线段树区间 合并、第k大)
    POJ2104-- K-th Number(主席树静态区间第k大)
    poj2409 & 2154 polya计数+欧拉函数优化
    CodeForces
    HDU
    HDU
    Gym
  • 原文地址:https://www.cnblogs.com/meteoric_cry/p/1851203.html
Copyright © 2011-2022 走看看