首先说明一下,在网上搜到的很多代码是如何获取输入光标位置的如下代码是如何获取光标的的字符位置,如对于串“He|llo World!”返回的是光标|前的字符数2,并不是光标在页面上的像素位置。当然,这段代码对于获取光标的像素位置能起到一定的辅助作用。
对于IE浏览器,通过下面的代码1实现起来比较方便。// 获取光标在文本框的位置
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++;
};
}
else if (elem.nodeName === 'INPUT') {// input
Sel.moveStart('character',-elem.value.length);
index = Sel.text.length;
}
}
else if (elem.selectionStart || elem.selectionStart == '0') { // Firefox support
index = elem.selectionStart;
}
return (index);
}
而对于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 = '<font style=white-space:pre-wrap;> </font>';
var div = elem[cloneDiv] || document.createElement('div'),focus = elem[cloneFocus] || document.createElement('font');
var text = elem[cloneLeft] || document.createElement('font');
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(//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++;
};
}
else if (elem.nodeName === 'INPUT') {// input
Sel.moveStart('character',-elem.value.length);
index = Sel.text.length;
}
}
else if (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;
}
测试页面如下: