演示
我寫了一個函數,其行爲如預期。一個非常詳細的演示面板可以在這裏找到:小提琴:http://jsfiddle.net/56Rep/5/
在演示界面是不言自明的。
的要求,在這個問題會在我的函數中實現如下功能:
var pixelPosition = getTextBoundingRect(input, 6)
功能依賴
更新:該功能是純JavaScript,而不是依賴於任何插件或框架!
該函數假定存在getBoundingClientRect
方法。文本範圍在被支持時使用。否則,功能是使用我的功能邏輯實現的。
功能邏輯
本身包含幾個註釋的代碼。這一部分更深入細節。
- 一個臨時
<div>
容器被創建。
- 1 - 3
<span>
元素已創建。每個量程保存輸入值的一部分(偏移0到selectionStart
,selectionStart
到selectionEnd
,selectionEnd
到字符串結尾,只有第二個量程是平均的)。
- 來自輸入元素的幾個重要樣式屬性被複制到這些
<div>
和<span>
標籤中。只有重要的樣式屬性被複制。例如,color
未被複制,因爲它不會以任何方式影響字符的偏移量。 #1
<div>
位於文本節點(輸入值)的確切位置。考慮到邊界和填充,以確保臨時<div>
是正確定位。
- 創建了一個變量,該變量保存的返回值爲
div.getBoundingClientRect()
。
- 臨時
<div>
被刪除,除非參數debug
設置爲true。
- 該函數返回
ClientRect
對象。有關此對象的更多信息,請參閱this page。 demo也顯示屬性列表:top
,left
,right
, bottom
,height
和width
。
#1:getBoundingClientRect()
(和一些次要屬性)被用於確定所述輸入元件的位置。然後,添加填充和邊框寬度,以獲取文本節點的實際位置。
已知問題
遇到不一致的情況下,只有當getComputedStyle
返回錯誤值font-family
:當一個頁面已經沒有定義font-family
特性,computedStyle返回不正確的值(甚至螢火蟲遇到此問題;環境:Linux,Firefox 3.6.23,字體「Sans Serif」)。
在演示中可以看到,定位有時略微偏離(幾乎爲零,總是小於1像素)。
技術限制可防止腳本在內容移動時獲得文本片段的精確偏移量,例如,當輸入字段中的第一個可見字符不等於第一個值的字符時。
代碼
// @author Rob W http://stackoverflow.com/users/938089/rob-w
// @name getTextBoundingRect
// @param input Required HTMLElement with `value` attribute
// @param selectionStart Optional number: Start offset. Default 0
// @param selectionEnd Optional number: End offset. Default selectionStart
// @param debug Optional boolean. If true, the created test layer
// will not be removed.
function getTextBoundingRect(input, selectionStart, selectionEnd, debug) {
// Basic parameter validation
if(!input || !('value' in input)) return input;
if(typeof selectionStart == "string") selectionStart = parseFloat(selectionStart);
if(typeof selectionStart != "number" || isNaN(selectionStart)) {
selectionStart = 0;
}
if(selectionStart < 0) selectionStart = 0;
else selectionStart = Math.min(input.value.length, selectionStart);
if(typeof selectionEnd == "string") selectionEnd = parseFloat(selectionEnd);
if(typeof selectionEnd != "number" || isNaN(selectionEnd) || selectionEnd < selectionStart) {
selectionEnd = selectionStart;
}
if (selectionEnd < 0) selectionEnd = 0;
else selectionEnd = Math.min(input.value.length, selectionEnd);
// If available (thus IE), use the createTextRange method
if (typeof input.createTextRange == "function") {
var range = input.createTextRange();
range.collapse(true);
range.moveStart('character', selectionStart);
range.moveEnd('character', selectionEnd - selectionStart);
return range.getBoundingClientRect();
}
// createTextRange is not supported, create a fake text range
var offset = getInputOffset(),
topPos = offset.top,
leftPos = offset.left,
width = getInputCSS('width', true),
height = getInputCSS('height', true);
// Styles to simulate a node in an input field
var cssDefaultStyles = "white-space:pre;padding:0;margin:0;",
listOfModifiers = ['direction', 'font-family', 'font-size', 'font-size-adjust', 'font-variant', 'font-weight', 'font-style', 'letter-spacing', 'line-height', 'text-align', 'text-indent', 'text-transform', 'word-wrap', 'word-spacing'];
topPos += getInputCSS('padding-top', true);
topPos += getInputCSS('border-top-width', true);
leftPos += getInputCSS('padding-left', true);
leftPos += getInputCSS('border-left-width', true);
leftPos += 1; //Seems to be necessary
for (var i=0; i<listOfModifiers.length; i++) {
var property = listOfModifiers[i];
cssDefaultStyles += property + ':' + getInputCSS(property) +';';
}
// End of CSS variable checks
var text = input.value,
textLen = text.length,
fakeClone = document.createElement("div");
if(selectionStart > 0) appendPart(0, selectionStart);
var fakeRange = appendPart(selectionStart, selectionEnd);
if(textLen > selectionEnd) appendPart(selectionEnd, textLen);
// Styles to inherit the font styles of the element
fakeClone.style.cssText = cssDefaultStyles;
// Styles to position the text node at the desired position
fakeClone.style.position = "absolute";
fakeClone.style.top = topPos + "px";
fakeClone.style.left = leftPos + "px";
fakeClone.style.width = width + "px";
fakeClone.style.height = height + "px";
document.body.appendChild(fakeClone);
var returnValue = fakeRange.getBoundingClientRect(); //Get rect
if (!debug) fakeClone.parentNode.removeChild(fakeClone); //Remove temp
return returnValue;
// Local functions for readability of the previous code
function appendPart(start, end){
var span = document.createElement("span");
span.style.cssText = cssDefaultStyles; //Force styles to prevent unexpected results
span.textContent = text.substring(start, end);
fakeClone.appendChild(span);
return span;
}
// Computing offset position
function getInputOffset(){
var body = document.body,
win = document.defaultView,
docElem = document.documentElement,
box = document.createElement('div');
box.style.paddingLeft = box.style.width = "1px";
body.appendChild(box);
var isBoxModel = box.offsetWidth == 2;
body.removeChild(box);
box = input.getBoundingClientRect();
var clientTop = docElem.clientTop || body.clientTop || 0,
clientLeft = docElem.clientLeft || body.clientLeft || 0,
scrollTop = win.pageYOffset || isBoxModel && docElem.scrollTop || body.scrollTop,
scrollLeft = win.pageXOffset || isBoxModel && docElem.scrollLeft || body.scrollLeft;
return {
top : box.top + scrollTop - clientTop,
left: box.left + scrollLeft - clientLeft};
}
function getInputCSS(prop, isnumber){
var val = document.defaultView.getComputedStyle(input, null).getPropertyValue(prop);
return isnumber ? parseFloat(val) : val;
}
}
可能與您尋找的內容有關:http:// stackoverflow。com/questions/4085312/jquery-get-the-cursor-position-of-text-in-input-without-browser-specific-code – Jesse
我很確定簡短答案是「否」,但我沒有現在有時間進行闡述或研究。 –
@TimDown我已經實現了請求的行爲。既然你有很多經驗範圍(由於你的Rangy項目),你能檢查它嗎? –