开发者社区> meteoric> 正文

TextRange之插入表情

简介: SNS类或是微博类的产品一般都有一个功能:插入表情,如下所示:   重点:兼容IE与其它主流的浏览器,根据上一次选择的选区范围进行操作。 自己写了一个TextRange(参考了网上的一些例子和代码) /** * @author Meteoric_cry */ /**...
+关注继续查看

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="width: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; width:800px; margin-top:30px;">
	选区之前的内容:<input type="text" id="beforeTxt"	style="width: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 {width:475px; height:110px; word-break:break-all; word-wrap:break-word;}
	#facePanel {border-width:1px 2px 2px 1px; border-color: #CCCCCC; border-style:solid; -webkit-border-radius: 6px; -moz-border-radius:6px; padding:6px; width:330px; margin-top:5px; display:none;}
	#facePanel li{float:left; width:28px; height:28px; padding:0 5px 5px 0;}
	#facePanel li a {display:block; text-decoration:none; border:1px dashed #DDD; width: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>

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
18989 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
25232 0
如何设置阿里云服务器安全组?阿里云安全组规则详细解说
阿里云安全组设置详细图文教程(收藏起来) 阿里云服务器安全组设置规则分享,阿里云服务器安全组如何放行端口设置教程。阿里云会要求客户设置安全组,如果不设置,阿里云会指定默认的安全组。那么,这个安全组是什么呢?顾名思义,就是为了服务器安全设置的。安全组其实就是一个虚拟的防火墙,可以让用户从端口、IP的维度来筛选对应服务器的访问者,从而形成一个云上的安全域。
17200 0
阿里云服务器ECS远程登录用户名密码查询方法
阿里云服务器ECS远程连接登录输入用户名和密码,阿里云没有默认密码,如果购买时没设置需要先重置实例密码,Windows用户名是administrator,Linux账号是root,阿小云来详细说下阿里云服务器远程登录连接用户名和密码查询方法
21678 0
阿里云服务器安全组设置内网互通的方法
虽然0.0.0.0/0使用非常方便,但是发现很多同学使用它来做内网互通,这是有安全风险的,实例有可能会在经典网络被内网IP访问到。下面介绍一下四种安全的内网互联设置方法。 购买前请先:领取阿里云幸运券,有很多优惠,可到下文中领取。
19217 0
使用OpenApi弹性释放和设置云服务器ECS释放
云服务器ECS的一个重要特性就是按需创建资源。您可以在业务高峰期按需弹性的自定义规则进行资源创建,在完成业务计算的时候释放资源。本篇将提供几个Tips帮助您更加容易和自动化的完成云服务器的释放和弹性设置。
18714 0
+关注
423
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载