TextRange之插入表情
SNS类或是微博类的产品一般都有一个功能:插入表情,如下所示:
重点:兼容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> </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>