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>