JSFiddle - React, Tailwind, and code Playground
HTML
<!-- Version 2: Pure JavaScript, created at 31 oct 2011 -->
<a href="http://stackoverflow.com/questions/6930578/get-cursor-or-text-position-in-pixels-for-input-element/7948715#7948715" target="_blank">Documentation at Stack Overflow</a>. Created by <a href="http://stackoverflow.com/users/938089/rob-w" target="_blank">Rob W</a><br />
<input value="getBoundingClientRect() for text ranges at non-IE browsers" id="test" /><br />
<input value="6" id="start" /> < "selectionStart" (Edit CSS at the upper box<br />
<input value="6" id="end" /> < "selectionEnd" to test the code)<br />
<input type="button" onclick="bounding_blabla()" value="Get info" />
<label for="debug"><input type="checkbox" id="debug" /> Debugging mode</label>
<input type="button" value="Remove ranges" title="Click here to remove all non-removed elements. Normally, these temporary elements are immediately removed, but this debugging screen allows you to check the results." id="remove" onclick="allDivs('remove')" />
<input type="button" value="Move original element +/- 20px" title="Move the original element 20px to up/down" id="move" />
<br />
<span id="out">
Hit button "Get info" to activate the function.
<span style="color:blue">Note: When debugging mode is on,
appended "ranges" are not removed.
If the script is working correctly, you
cannot distinguish the range text from
the copied source, since the copy should
be an exact copy, including positioning.</span></span>
CSS
#test { position: relative; outline: 2px solid green; width: 80%; /* Previous styles = debugging */
top: 250px;
/* Examples: font-size, font-family, top, left, right, bottom, etc.*/
}
/*div { background: rgba(200, 0, 0, 0.4);*/
input[type=button]{padding:2px 4px;}
#out { white-space: pre; display: block; }
JavaScript
// @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;",
...