How do you get the cursor position in a textarea? - javascript

I have a textarea and I would like to know if I am on the last line in the textarea or the first line in the textarea with my cursor with JavaScript.
I thought of grabbing the position of the first newline character and the last newline character and then grabbing the position of the cursor.
var firstNewline = $('#myTextarea').val().indexOf('\n');
var lastNewline = $('#myTextarea').val().lastIndexOf('\n');
var cursorPosition = ?????;
if (cursorPosition < firstNewline)
// I am on first line.
else if (cursorPosition > lastNewline)
// I am on last line.
Is it possible to grab the cursor position within the textarea?
Do you have a better suggestion for finding out if I am on the first or last line of a textarea?
jQuery solutions preferred unless JavaScript is as simple or simpler.

If there is no selection, you can use the properties .selectionStart or .selectionEnd (with no selection they're equal).
var cursorPosition = $('#myTextarea').prop("selectionStart");
Note that this is not supported in older browsers, most notably IE8-. There you'll have to work with text ranges, but it's a complete frustration.
I believe there is a library somewhere which is dedicated to getting and setting selections/cursor positions in input elements, though. I can't recall its name, but there seem to be dozens on articles about this subject.

Here's a cross browser function I have in my standard library:
function getCursorPos(input) {
if ("selectionStart" in input && document.activeElement == input) {
return {
start: input.selectionStart,
end: input.selectionEnd
};
}
else if (input.createTextRange) {
var sel = document.selection.createRange();
if (sel.parentElement() === input) {
var rng = input.createTextRange();
rng.moveToBookmark(sel.getBookmark());
for (var len = 0;
rng.compareEndPoints("EndToStart", rng) > 0;
rng.moveEnd("character", -1)) {
len++;
}
rng.setEndPoint("StartToStart", input.createTextRange());
for (var pos = { start: 0, end: len };
rng.compareEndPoints("EndToStart", rng) > 0;
rng.moveEnd("character", -1)) {
pos.start++;
pos.end++;
}
return pos;
}
}
return -1;
}
Use it in your code like this:
var cursorPosition = getCursorPos($('#myTextarea')[0])
Here's its complementary function:
function setCursorPos(input, start, end) {
if (arguments.length < 3) end = start;
if ("selectionStart" in input) {
setTimeout(function() {
input.selectionStart = start;
input.selectionEnd = end;
}, 1);
}
else if (input.createTextRange) {
var rng = input.createTextRange();
rng.moveStart("character", start);
rng.collapse();
rng.moveEnd("character", end - start);
rng.select();
}
}
http://jsfiddle.net/gilly3/6SUN8/

Here is code to get line number and column position
function getLineNumber(tArea) {
return tArea.value.substr(0, tArea.selectionStart).split("\n").length;
}
function getCursorPos() {
var me = $("textarea[name='documenttext']")[0];
var el = $(me).get(0);
var pos = 0;
if ('selectionStart' in el) {
pos = el.selectionStart;
} else if ('selection' in document) {
el.focus();
var Sel = document.selection.createRange();
var SelLength = document.selection.createRange().text.length;
Sel.moveStart('character', -el.value.length);
pos = Sel.text.length - SelLength;
}
var ret = pos - prevLine(me);
alert(ret);
return ret;
}
function prevLine(me) {
var lineArr = me.value.substr(0, me.selectionStart).split("\n");
var numChars = 0;
for (var i = 0; i < lineArr.length-1; i++) {
numChars += lineArr[i].length+1;
}
return numChars;
}
tArea is the text area DOM element

Related

How do you get and set the caret position in a contenteditable?

This question has some answers here but there's a few problems.
Basically I want to do the following:
get caret position
set innerHTML of the contenteditable (this resets the caret position)
set the caret position to the value obtained in step 1.
A lot of the existing answers seem to be complicated by cross-browser support but I only need it to work on modern chrome. It also needs to work with html. Ideally it would look exactly like this:
var index = getCaretPosition(contentEditableDiv);
onEdit(contentEditableDiv); // <-- callback function that manipulates the text and sets contentEditableDiv.innerHTML = theManipulatedText
setCaretPosition(contentEditableDiv, index);
I've tried looking through the documentation but it's not straightforward and I think this question is due for a leaner answer anyways.
This seems to work for me but I've only tested it for my use cases.
GET
function getCaretIndex(win, contentEditable) {
var index = 0;
var selection = win.getSelection();
var textNodes = textNodesUnder(contentEditable);
for(var i = 0; i < textNodes.length; i++) {
var node = textNodes[i];
var isSelectedNode = node === selection.focusNode;
if(isSelectedNode) {
index += selection.focusOffset;
break;
}
else {
index += node.textContent.length;
}
}
return index;
}
SET
function setCaretIndex(win, contentEditable, newCaretIndex) {
var cumulativeIndex = 0;
var relativeIndex = 0;
var targetNode = null;
var textNodes = textNodesUnder(contentEditable);
for(var i = 0; i < textNodes.length; i++) {
var node = textNodes[i];
if(newCaretIndex <= cumulativeIndex + node.textContent.length) {
targetNode = node;
relativeIndex = newCaretIndex - cumulativeIndex;
break;
}
cumulativeIndex += node.textContent.length;
}
var range = win.document.createRange();
range.setStart(targetNode, relativeIndex);
range.setEnd(targetNode, relativeIndex);
range.collapse();
var sel = win.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
REQUIRED HELPER
function textNodesUnder(node) { // https://stackoverflow.com/a/10730777/3245937
var all = [];
for (node=node.firstChild;node;node=node.nextSibling){
if (node.nodeType==3) {
all.push(node);
}
else {
all = all.concat(textNodesUnder(node));
}
}
return all;
}
TEST (just call this function)
It loops through the text in a contenteditable, sets the caret index, then reads it. Console output is: (setIndex | getIndex)
function testContentEditable() {
document.body.innerHTML = "<div contenteditable></div>"
var ce = document.querySelector("[contenteditable]");
ce.focus();
ce.innerHTML = "HELLO <span data-foo='true' style='text-decoration: underline;'><span style='color:red;'>WORLD</span> MY</span> NAME IS BOB";
var i = 0;
var intv = setInterval(function() {
if(i == ce.innerText.length) {
clearInterval(intv);
}
setCaretIndex(window, ce, i);
var currentIndex = getCaretIndex(window, ce);
console.log(i + " | " + currentIndex);
i++;
}, 100);
}
FIDDLE
function getCaretIndex(win, contentEditable) {
var index = 0;
var selection = win.getSelection();
var textNodes = textNodesUnder(contentEditable);
for(var i = 0; i < textNodes.length; i++) {
var node = textNodes[i];
var isSelectedNode = node === selection.focusNode;
if(isSelectedNode) {
index += selection.focusOffset;
break;
}
else {
index += node.textContent.length;
}
}
return index;
}
function setCaretIndex(win, contentEditable, newCaretIndex) {
var cumulativeIndex = 0;
var relativeIndex = 0;
var targetNode = null;
var textNodes = textNodesUnder(contentEditable);
for(var i = 0; i < textNodes.length; i++) {
var node = textNodes[i];
if(newCaretIndex <= cumulativeIndex + node.textContent.length) {
targetNode = node;
relativeIndex = newCaretIndex - cumulativeIndex;
break;
}
cumulativeIndex += node.textContent.length;
}
var range = win.document.createRange();
range.setStart(targetNode, relativeIndex);
range.setEnd(targetNode, relativeIndex);
range.collapse();
var sel = win.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
function textNodesUnder(node) { // https://stackoverflow.com/a/10730777/3245937
var all = [];
for (node=node.firstChild;node;node=node.nextSibling){
if (node.nodeType==3) {
all.push(node);
}
else {
all = all.concat(textNodesUnder(node));
}
}
return all;
}
function testContentEditable() {
document.body.innerHTML = "<div contenteditable></div>"
var ce = document.querySelector("[contenteditable]");
ce.focus();
ce.innerHTML = "HELLO <span data-foo='true' style='text-decoration: underline;'><span style='color:red;'>WORLD</span> MY</span> NAME IS BOB";
var i = 0;
var intv = setInterval(function() {
if(i == ce.innerText.length) {
clearInterval(intv);
}
setCaretIndex(window, ce, i);
var currentIndex = getCaretIndex(window, ce);
console.log(i + " | " + currentIndex);
i++;
}, 100);
}
testContentEditable();

How to highlight first occurrence of a string in a UIWebView?

I use the javascript code below to find all occurrences of a string in UIWebView:
UIWebViewSearch.js:
var uiWebview_SearchResultCount = 0;
/*!
#method uiWebview_HighlightAllOccurencesOfStringForElement
#abstract // helper function, recursively searches in elements and their child nodes
#discussion // helper function, recursively searches in elements and their child nodes
element - HTML elements
keyword - string to search
*/
function uiWebview_HighlightAllOccurencesOfStringForElement(element,keyword) {
if (element) {
if (element.nodeType == 3) { // Text node
var count = 0;
var elementTmp = element;
while (true) {
var value = elementTmp.nodeValue; // Search for keyword in text node
var idx = value.toLowerCase().indexOf(keyword);
if (idx < 0) break;
count++;
elementTmp = document.createTextNode(value.substr(idx+keyword.length));
}
uiWebview_SearchResultCount += count;
var index = uiWebview_SearchResultCount;
while (true) {
var value = element.nodeValue; // Search for keyword in text node
var idx = value.toLowerCase().indexOf(keyword);
if (idx < 0) break; // not found, abort
//we create a SPAN element for every parts of matched keywords
var span = document.createElement("span");
var text = document.createTextNode(value.substr(idx,keyword.length));
span.appendChild(text);
span.setAttribute("class","uiWebviewHighlight");
span.style.backgroundColor="yellow";
span.style.color="black";
index--;
span.setAttribute("id", "SEARCH WORD"+(index));
//span.setAttribute("id", "SEARCH WORD"+uiWebview_SearchResultCount);
//element.parentNode.setAttribute("id", "SEARCH WORD"+uiWebview_SearchResultCount);
//uiWebview_SearchResultCount++; // update the counter
text = document.createTextNode(value.substr(idx+keyword.length));
element.deleteData(idx, value.length - idx);
var next = element.nextSibling;
//alert(element.parentNode);
element.parentNode.insertBefore(span, next);
element.parentNode.insertBefore(text, next);
element = text;
}
} else if (element.nodeType == 1) { // Element node
if (element.style.display != "none" && element.nodeName.toLowerCase() != 'select') {
for (var i=element.childNodes.length-1; i>=0; i--) {
uiWebview_HighlightAllOccurencesOfStringForElement(element.childNodes[i],keyword);
}
}
}
}
}
// the main entry point to start the search
function uiWebview_HighlightAllOccurencesOfString(keyword) {
uiWebview_RemoveAllHighlights();
uiWebview_HighlightAllOccurencesOfStringForElement(document.body, keyword.toLowerCase());
}
// helper function, recursively removes the highlights in elements and their childs
function uiWebview_RemoveAllHighlightsForElement(element) {
if (element) {
if (element.nodeType == 1) {
if (element.getAttribute("class") == "uiWebviewHighlight") {
var text = element.removeChild(element.firstChild);
element.parentNode.insertBefore(text,element);
element.parentNode.removeChild(element);
return true;
} else {
var normalize = false;
for (var i=element.childNodes.length-1; i>=0; i--) {
if (uiWebview_RemoveAllHighlightsForElement(element.childNodes[i])) {
normalize = true;
}
}
if (normalize) {
element.normalize();
}
}
}
}
return false;
}
// the main entry point to remove the highlights
function uiWebview_RemoveAllHighlights() {
uiWebview_SearchResultCount = 0;
uiWebview_RemoveAllHighlightsForElement(document.body);
}
function uiWebview_ScrollTo(idx) {
var scrollTo = document.getElementById("SEARCH WORD" + idx);
if (scrollTo) scrollTo.scrollIntoView();
}
And in my ViewController.swift file, I load the javascript file and perform a search like so:
func highlightAllOccurencesOfString(str:String) -> Int {
let path = Bundle.main.path(forResource: "UIWebViewSearch", ofType: "js")
var jsCode = ""
do{
jsCode = try String(contentsOfFile: path!)
myWebView.stringByEvaluatingJavaScript(from: jsCode)
let startSearch = "uiWebview_HighlightAllOccurencesOfString('\(str)')"
myWebView.stringByEvaluatingJavaScript(from: startSearch)
let result = myWebView.stringByEvaluatingJavaScript(from: "uiWebview_SearchResultCount")!
}catch
{
// do something
}
return Int(result)!
}
This finds and highlights all occurrences of a string within the webview, but I only want the first occurrence highlighted.
For example, with this code when I call:
highlightAllOccurencesOfString(str:"Hello")
All instances of "Hello" gets highlighted in the webview:
Hello Frank, Hello Noah
But I want this result:
Hello Frank, Hello Noah
How do I modify the javascript code to highlight only the first occurrence of a searched string?
UPDATE: I tried JonLuca's answer below but nothing got highlighted.
Any help would be greatly appreciated
The function is operating like a tree, where it checks if the element is a text node, and if not it calls itself on every one of its children.
You have to let the caller know that you found a single element to highlight, and then leave.
Also, the way this searches through it does not guarantee that what you consider is the "first" occurrence of the word will be the first occurence within the document.body tree.
Within the code, have it return true if it found a word to highlight, and false otherwise. That'll prevent it from continuing its search. Modify the js like so:
function uiWebview_HighlightAllOccurencesOfStringForElement(element, keyword) {
if (element) {
if (element.nodeType == 3) { // Text node
var count = 0;
var elementTmp = element;
var value = elementTmp.nodeValue; // Search for keyword in text node
var idx = value.toLowerCase().indexOf(keyword);
if (idx < 0) {
return false;
}
count++;
elementTmp = document.createTextNode(value.substr(idx + keyword.length));
uiWebview_SearchResultCount += count;
var index = uiWebview_SearchResultCount;
var value = element.nodeValue; // Search for keyword in text node
var idx = value.toLowerCase().indexOf(keyword);
if (idx < 0) {
return false;
} // not found, abort
//we create a SPAN element for every parts of matched keywords
var span = document.createElement("span");
var text = document.createTextNode(value.substr(idx, keyword.length));
span.appendChild(text);
span.setAttribute("class", "uiWebviewHighlight");
span.style.backgroundColor = "yellow";
span.style.color = "black";
index--;
span.setAttribute("id", "SEARCH WORD" + (index));
//span.setAttribute("id", "SEARCH WORD"+uiWebview_SearchResultCount);
//element.parentNode.setAttribute("id", "SEARCH WORD"+uiWebview_SearchResultCount);
//uiWebview_SearchResultCount++; // update the counter
text = document.createTextNode(value.substr(idx + keyword.length));
element.deleteData(idx, value.length - idx);
var next = element.nextSibling;
//alert(element.parentNode);
element.parentNode.insertBefore(span, next);
element.parentNode.insertBefore(text, next);
element = text;
return true;
} else if (element.nodeType == 1) { // Element node
if (element.style.display != "none" && element.nodeName.toLowerCase() != 'select') {
for (var i = element.childNodes.length - 1; i >= 0; i--) {
if (uiWebview_HighlightAllOccurencesOfStringForElement(element.childNodes[i], keyword)) {
return true;
}
}
}
}
}
}
This isn't guaranteed to be correct, or that it covers all of your use cases. However, I had it return true on the first occurrence of finding the highlighted word. It should stop executing and not highlight any other words.
Let me know if this works or not - I don't have the ability to test it right now.

How to get neighbor character from selected text?

I have a string like this:
var comment = 'this is a test';
Assume this i is selected, Now I need to null (left side) and s (right side). How can I get them?
I can get selected text like this:
function getSelectionHtml() {
var html = "";
if (typeof window.getSelection != "undefined") {
var sel = window.getSelection();
if (sel.rangeCount) {
var container = document.createElement("div");
for (var i = 0, len = sel.rangeCount; i < len; ++i) {
container.appendChild(sel.getRangeAt(i).cloneContents());
}
html = container.innerHTML;
}
} else if (typeof document.selection != "undefined") {
if (document.selection.type == "Text") {
html = document.selection.createRange().htmlText;
}
}
return html;
}
var selected_text = getSelectionHtml();
document.addEventListener("click", function() {
var selection = window.getSelection();
// Check if there are any ranges selected.
if (selection.rangeCount > 0 && selection.type == "Range") {
// Text content of the element.
var text = selection.anchorNode.textContent;
// selection.anchorOffset is the start position of the selection
var before = text.substring(selection.anchorOffset-1, selection.anchorOffset);
// selection.extentOffset is the end position of the selection
var after = text.substring(selection.extentOffset, selection.extentOffset+1);
// Check if there are any letters before or after selected string.
// If not, change before and after to null.
before = before.length === 1 ? before : null;
after = after.length === 1 ? after : null;
console.log(before, after);
}
});
<div>this is a test</div>
To get two characters:
document.addEventListener("click", function() {
var selection = window.getSelection();
// Check if there are any ranges selected.
if (selection.rangeCount > 0 && selection.type == "Range") {
// Text content of the element.
var text = selection.anchorNode.textContent;
// selection.anchorOffset is the start position of the selection
var before = text.substring(selection.anchorOffset-2, selection.anchorOffset);
// selection.extentOffset is the end position of the selection
var after = text.substring(selection.extentOffset, selection.extentOffset+2);
// Check if there are any letters before or after selected string.
// If not, change before and after to null.
before = before.length >= 1 ? before : null;
after = after.length >= 1 ? after : null;
console.log(before, after);
}
});
<div>this is a test</div>
You don't need jQuery for this. All you need is window.getSelection(). This returns an object. You can get the surrounding text with window.getSelection().anchorNode.data, and get the index of the selection within that text using window.getSelection().anchorOffset. Putting this all together, we have
var selection = window.getSelection();
var selectionText = selection.toString();
var surroundingText = selection.anchorNode.data;
var index = selection.anchorOffset;
var leftNeighbor = surroundingText[index - 1];
var rightNeighbor = surroundingText[index + selectionText.length];
Note that you will get undefined instead of null when there is no neighbor character.
window.addEventListener("click", function(){
var selection = window.getSelection();
var selectionText = selection.toString();
var surroundingText = selection.anchorNode.data;
var index = selection.anchorOffset;
var leftNeighbor = surroundingText[index - 1];
var rightNeighbor = surroundingText[index + selectionText.length];
alert(leftNeighbor + " " + rightNeighbor);
});
<div>
this is a test
</div>
The problem with the other two solutions is they both work if the user selects from left to right, but not if they select right to left. As such, I use this modified version:
const selection = window.getSelection();
const text = selection.anchorNode.textContent;
let beforeChar;
let afterChar;
if (selection.anchorOffset < selection.focusOffset) {
beforeChar = text.substring(selection.anchorOffset, selection.anchorOffset-1);
afterChar = text.substring(selection.extentOffset, selection.extentOffset+1);
} else {
beforeChar = text.substring(selection.extentOffset, selection.extentOffset-1)
afterChar = text.substring(selection.anchorOffset, selection.anchorOffset+1);
}
console.log(beforeChar, afterChar);

JavaScript window.find() does not select search term

When I try to pass text which spreads throughout a few block elements the window.find method dosent work:
HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
</head>
<body>
<p>search me</p><b> I could be the answer</b>
</body>
</html>
JavaScript:
window.find("meI could be");
Or:
str = "me";
str+= "\n";
str+="I could be t";
window.find(str);
This happens when the <p> element is present between the search term.
The end result should be a GUI selection on the text in the page, I do not want to search if it exists.
I would like to know how to achieve this in Firefox(at least) and internet explorer.
Note: I can't change the dom (e.g. change to display inline).
Edit:
Here is something I tried after #Alexey Lebedev's comment, but it also finds the script (tag [...] text):
Can I make it more simple? (better)?
function nativeTreeWalker(startNode) {
var walker = document.createTreeWalker(
startNode,
NodeFilter.SHOW_TEXT,
null,
false
);
var node;
var textNodesV = [];
var textNodes = [];
node = walker.nextNode();
while(node ) {
if(node.nodeValue.trim()){
textNodes.push(node);
textNodesV.push(node.nodeValue);
//console.log(node.nodeValue);
}
node = walker.nextNode();
}
return [textNodes,textNodesV];
}
var result = nativeTreeWalker(document.body);
var textNodes = result[0];
var textNodesV = result[1];
var param = " Praragraph.Test 3 Praragr";
paramArr = param.split(/(?=[\S])(?!\s)(?=[\W])(?=[\S])/g);
//Fix split PARAM
for(i=0;i<paramArr.length-1;i++){
paramArr[i]= paramArr[i]+paramArr[i+1].charAt(0);
paramArr[i+1] = paramArr[i+1].substring(1,paramArr[i+1].length);
}
//Fix last element PARAM
if(paramArr[paramArr.length-1] === ""){
paramArr.splice(paramArr.length-1,1);
}
//console.log(paramArr);
var startNode,startOffset,sFound=false,
endNode,endOffset;
for(i=0;i<paramArr.length;i++){
for(j=0;j<textNodesV.length;j++){
//Fully Equal
var pos = textNodesV[j].indexOf(paramArr[i]);
if(pos != -1){
if(!sFound){
startNode = textNodes[j];
startOffset = pos;
sFound=true;
}else{
endNode = textNodes[j];
endOffset = pos+paramArr[i].length;
break;
}
}
}
}
console.log(startNode);
console.log(startOffset);
console.log(endNode);
console.log(endOffset);
var range = document.createRange();
range.setStart(startNode,startOffset);
range.setEnd(endNode,endOffset);
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
Note: No jQuery (only Raw JS).
JS Bin demo: http://jsbin.com/aqiciv/1/
If you want this to work in IE < 9 you'll need to add MS-specific selection code (nightmare), or use Rangy.js (pretty heavy).
function visibleTextNodes() {
var walker = document.createTreeWalker(
document.body,
NodeFilter.SHOW_ALL,
function(node) {
if (node.nodeType == 3) {
return NodeFilter.FILTER_ACCEPT;
} else if (node.offsetWidth && node.offsetHeight && node.style.visibility != 'hidden') {
return NodeFilter.FILTER_SKIP;
} else {
return NodeFilter.FILTER_REJECT;
}
},
false
);
for (var nodes = []; walker.nextNode();) {
nodes.push(walker.currentNode);
}
return nodes;
}
// Find the first match, select and scroll to it.
// Case- and whitespace- insensitive.
// For better scrolling to selection see https://gist.github.com/3744577
function highlight(needle) {
needle = needle.replace(/\s/g, '').toLowerCase();
var textNodes = visibleTextNodes();
for (var i = 0, texts = []; i < textNodes.length; i++) {
texts.push(textNodes[i].nodeValue.replace(/\s/g, '').toLowerCase());
}
var matchStart = texts.join('').indexOf(needle);
if (matchStart < 0) {
return false;
}
var nodeAndOffsetAtPosition = function(position) {
for (var i = 0, consumed = 0; consumed + texts[i].length < position; i++) {
consumed += texts[i].length;
}
var whitespacePrefix = textNodes[i].nodeValue.match(/^\s*/)[0];
return [textNodes[i], position - consumed + whitespacePrefix.length];
};
var range = document.createRange();
range.setStart.apply(range, nodeAndOffsetAtPosition(matchStart));
range.setEnd.apply( range, nodeAndOffsetAtPosition(matchStart + needle.length));
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
range.startContainer.parentNode.scrollIntoView();
}
highlight('hello world');

how can I get the Character offset related to body with Range Object and window.getSelection()?

I'm writing a client-side application with Javascript, I'm using the following functions :
function creation() {
var userSelection;
if (window.getSelection) {
userSelection = window.getSelection();
}
else if (document.selection) { // should come last; Opera!
userSelection = document.selection.createRange();
}
var rangeObject = getRangeObject(userSelection);
var startOffset = rangeObject.startOffset;
var endOffset = rangeObject.endOffset;
var startCon = rangeObject.startContainer;
var endCon = rangeObject.endContainer;
var myRange = document.createRange();
myRange.setStart(startCon,rangeObject.startOffset);
myRange.setEnd(endCon, rangeObject.endOffset);
$('#result').text(myRange.toString());
}
function getRangeObject(selectionObject) {
if (selectionObject.getRangeAt) {
var ret = selectionObject.getRangeAt(0);
return ret;
}
else { // Safari!
var range = document.createRange();
range.setStart(selectionObject.anchorNode, selectionObject.anchorOffset);
range.setEnd(selectionObject.focusNode, selectionObject.focusOffset);
return range;
}
}
I need a way to know the character offset related to body element. I found a function with counts the character in an element :
function getCharacterOffsetWithin(range, node) {
var treeWalker = document.createTreeWalker(
node,
NodeFilter.SHOW_TEXT,
function (node) {
var nodeRange = document.createRange();
nodeRange.selectNode(node);
return nodeRange.compareBoundaryPoints(Range.END_TO_END, range) < 1 ?
NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
},
false
);
var charCount = 0;
while (treeWalker.nextNode()) {
charCount += treeWalker.currentNode.length;
}
if (range.startContainer.nodeType == 3) {
charCount += range.startOffset;
}
return charCount;
}
That looks like a function from one of my answers. I overcomplicated it a little; see this answer for a simpler function that works in IE < 9 as well: https://stackoverflow.com/a/4812022/96100. You can just pass in document.body as the node parameter. Also, please read the part in the linked answer about the shortcomings of this approach.
Here's a live demo: http://jsfiddle.net/PzQjA/

Categories

Resources