【简介】本文主要实现获取textarea和input光标的像素位置,即光标的offsetLeft与offsetTop。可实现如下效果:


首先说明一下,在网上搜到的很多代码是如何获取输入光标位置的如下代码是如何获取光标的的字符位置,如对于串“He|llo World!”返回的是光标|前的字符数2,并不是光标在页面上的像素位置。当然,这段代码对于获取光标的像素位置能起到一定的辅助作用。
// 获取光标在文本框的位置
function _getFocus(elem) {
var index =0;
if (document.selection) {// IE Support
elem.focus();
var Sel = document.selection.createRange();
if (elem.nodeName ==='TEXTAREA') {//textarea
var Sel2 = Sel.duplicate();
Sel2.moveToElementText(elem);
var index =-1;
while (Sel2.inRange(Sel)) {
Sel2.moveStart('character');
index++;
};
}
elseif (elem.nodeName ==='INPUT') {// input
Sel.moveStart('character', -elem.value.length);
index = Sel.text.length;
}
}
elseif (elem.selectionStart || elem.selectionStart =='0') { // Firefox support
index = elem.selectionStart;
}
return (index);
}
对于IE浏览器,通过下面的代码1实现起来比较方便。
//代码1
if (document.selection) {
elem.focus();
var Sel = document.selection.createRange();
return {
left: Sel.boundingLeft,
top: Sel.boundingTop,
bottom: Sel.boundingTop + Sel.boundingHeight
};
}
而 对于firefox之类的浏览器则通过模拟来实现,如下图所示。首先通过拷贝输入区域的样式到一个div层(clone层),然后在此clone层之中的 text子层添加光标之前的字符,并在text子层之后添加focus层,focus层中包含字符“|”来模拟光标,进而通过获取focus层的偏移量即 可获得文本光标的像素坐标位置。

具体实现代码如下:
var kingwolfofsky = {
/**
* 获取输入光标在页面中的坐标
* @param {HTMLElement} 输入框元素
* @return {Object} 返回left和top,bottom
*/
getInputPositon: function (elem) {
if (document.selection) { //IE Support
elem.focus();
var Sel = document.selection.createRange();
return {
left: Sel.boundingLeft,
top: Sel.boundingTop,
bottom: Sel.boundingTop + Sel.boundingHeight
};
} else {
var that =this;
var cloneDiv ='{$clone_div}', cloneLeft ='{$cloneLeft}', cloneFocus ='{$cloneFocus}', cloneRight ='{$cloneRight}';
var none ='<span style="white-space:pre-wrap;"> </span>';
var div = elem[cloneDiv] || document.createElement('div'), focus = elem[cloneFocus] || document.createElement('span');
var text = elem[cloneLeft] || document.createElement('span');
var offset = that._offset(elem), index =this._getFocus(elem), focusOffset = { left: 0, top: 0 };
if (!elem[cloneDiv]) {
elem[cloneDiv] = div, elem[cloneFocus] = focus;
elem[cloneLeft] = text;
div.appendChild(text);
div.appendChild(focus);
document.body.appendChild(div);
focus.innerHTML ='|';
focus.style.cssText ='display:inline-block;width:0px;overflow:hidden;z-index:-100;word-wrap:break-word;word-break:break-all;';
div.className =this._cloneStyle(elem);
div.style.cssText ='visibility:hidden;display:inline-block;position:absolute;z-index:-100;word-wrap:break-word;word-break:break-all;overflow:hidden;';
};
div.style.left =this._offset(elem).left +"px";
div.style.top =this._offset(elem).top +"px";
var strTmp = elem.value.substring(0, index).replace(/</g, '<').replace(/>/g, '>').replace(/\n/g, '<br/>').replace(/\s/g, none);
text.innerHTML = strTmp;
focus.style.display ='inline-block';
try { focusOffset =this._offset(focus); } catch (e) { };
focus.style.display ='none';
return {
left: focusOffset.left,
top: focusOffset.top,
bottom: focusOffset.bottom
};
}
},
// 克隆元素样式并返回类
_cloneStyle: function (elem, cache) {
if (!cache && elem['${cloneName}']) return elem['${cloneName}'];
var className, name, rstyle =/^(number|string)$/;
var rname =/^(content|outline|outlineWidth)$/; //Opera: content; IE8:outline && outlineWidth
var cssText = [], sStyle = elem.style;
for (name in sStyle) {
if (!rname.test(name)) {
val =this._getStyle(elem, name);
if (val !==''&& rstyle.test(typeof val)) { // Firefox 4
name = name.replace(/([A-Z])/g, "-$1").toLowerCase();
cssText.push(name);
cssText.push(':');
cssText.push(val);
cssText.push(';');
};
};
};
cssText = cssText.join('');
elem['${cloneName}'] = className ='clone'+ (new Date).getTime();
this._addHeadStyle('.'+ className +'{'+ cssText +'}');
return className;
},
// 向页头插入样式
_addHeadStyle: function (content) {
var style =this._style[document];
if (!style) {
style =this._style[document] = document.createElement('style');
document.getElementsByTagName('head')[0].appendChild(style);
};
style.styleSheet && (style.styleSheet.cssText += content) || style.appendChild(document.createTextNode(content));
},
_style: {},
// 获取最终样式
_getStyle: 'getComputedStyle'in window ?function (elem, name) {
return getComputedStyle(elem, null)[name];
} : function (elem, name) {
return elem.currentStyle[name];
},
// 获取光标在文本框的位置
_getFocus: function (elem) {
var index =0;
if (document.selection) {// IE Support
elem.focus();
var Sel = document.selection.createRange();
if (elem.nodeName ==='TEXTAREA') {//textarea
var Sel2 = Sel.duplicate();
Sel2.moveToElementText(elem);
var index =-1;
while (Sel2.inRange(Sel)) {
Sel2.moveStart('character');
index++;
};
}
elseif (elem.nodeName ==='INPUT') {// input
Sel.moveStart('character', -elem.value.length);
index = Sel.text.length;
}
}
elseif (elem.selectionStart || elem.selectionStart =='0') { // Firefox support
index = elem.selectionStart;
}
return (index);
},
// 获取元素在页面中位置
_offset: function (elem) {
var box = elem.getBoundingClientRect(), doc = elem.ownerDocument, body = doc.body, docElem = doc.documentElement;
var clientTop = docElem.clientTop || body.clientTop ||0, clientLeft = docElem.clientLeft || body.clientLeft ||0;
var top = box.top + (self.pageYOffset || docElem.scrollTop) - clientTop, left = box.left + (self.pageXOffset || docElem.scrollLeft) - clientLeft;
return {
left: left,
top: top,
right: left + box.width,
bottom: top + box.height
};
}
};
function getPosition(ctrl) {
var p = kingwolfofsky.getInputPositon(ctrl);
document.getElementById('show').style.left = p.left +"px";
document.getElementById('show').style.top = p.bottom +"px";
}
测试页面如下:
<!DOCTYPE html>
<html>
<head>
<title>InputPostion</title>
<script type="text/javascript" src="kingwolfofsky.js""></script>
<script type="text/javascript">
function show(elem) {
var p = kingwolfofsky.getInputPositon(elem);
var s = document.getElementById('show');
s.style.top = p.bottom+'px';
s.style.left = p.left +'px';
s.style.display ='inherit';
}
</script>
</head>
<body>
<textarea id="text" onkeyup="show(this)" style="width: 340px; height: 210px;"></textarea>
<br />
<input type="text" onkeyup="show(this)" style="width: 340px;"/>
<div id="show" style="width: 34px; height: 13px; background: #eee; position: absolute;border:1px solid grey;font-size:13px; display:none;">Tips</div>
</body>
</html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>js面向对象编程之计算器</title>
<style type="text/css">
html,body {font-size:12px; font-family:Consolas;}
div {margin-left:auto; margin-right:auto; margin-top:0px; margin-bottom:0px;}
a {text-decoration:none; color:#666}
#caculator {width:170px; border:1px solid #CCC; text-align:center;padding:2px;}
#pannel > a:hover {background-color:#FFF;}
</style>
<script type="text/javascript">
function caculator()
{
this.expression = '';
this.clearFlag = true;
};
caculator.prototype = {
createButton:function(lb)
{
var bt = this.create('a');
bt.style.cssText = 'cursor:pointer;padding:2px 4px;border:1px solid #CCC; margin:2px; width:20px; float:left; background-color:#EFEFEF; text-align:center;';
bt.innerHTML = lb;
return bt;
},
init:function()
{
var _this = this;
var btn = [['7','8','9','/','C'],['4','5','6','x','←'],['1','2','3','-','.'],['(','0',')','+','=']];
var lo = this.get('pannel');
var bt = null;
for(var i in btn)
{
for(var k in btn[i])
{
bt = this.createButton(btn[i][k]);
this.addActionListener(bt,'click',function(e){ var evt = window.event || e; _this.clickEvent(e);});
lo.appendChild(bt);
}
}
},
get:function(i)
{
return typeof(i) == 'string'?document.getElementById(i):i;
},
create:function(tagName)
{
return document.createElement(tagName);
},
addActionListener:function(o,evt,callback)
{
if(window.addEventListener)
{
o.addEventListener(evt,callback,false);
}else if(window.attachEvent){
o.attachEvent('on' + evt,callback);
}else{
o['on' + evt] = callback;
}
},
//获取点击事件元素并计算
clickEvent:function(e)
{
var o = e.srcElement || e.target;
var v = o.innerHTML;
var l = this.expression.length;
var i = this.get('show');
var m = i.value;
m;
var n = m.length;
switch(v)
{
case '←':
this.expression = l > 1?this.expression.substr(0,l - 1):'0';
i.value = n > 1?m.substr(0,n - 1):'0';
if(i.value == '0') this.clearFlag = true;
break;
case '=':
this.expression = this.expression.replace('x','*');
i.value = eval(this.expression);
this.clearFlag = true;
break;
case 'C':
i.value = '0';
this.expression = '';
this.clearFlag = true;
break;
default:
this.expression += v;
var val = parseInt(m);
if(v != '+' && v != '-' && v != 'x' && v != '/' && v != '(' && v != ')')
{
i.value = this.clearFlag?v:(m + '' + v);
this.clearFlag = false;
return;
}
this.clearFlag = true;
}
}
}
var initCaculator = function()
{
var cal = new caculator();
cal.init();
}
</script>
</head>
<body onload="initCaculator();">
<div id="caculator">
<div id="result"><input type="text" size="22" readonly="readonly" id="show" name="show" value="0" style="width:162px;" /></div>
<div id="pannel"></div>
<div style="clear:both;"></div>
</div>
</body>
</html>
转的别人的,留着研究下。