Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get caret index in contenteditable div including tags

I have a contentEditable div in which I have multiple tags (br, b, u, i) and text.

I need to get the caret index position relative to the div, including all the tags.

For example:

<div id="h" contenteditable="true">abc<b>def<br>ghi</b>jkl</div>

If the cursor is between g and h, I need the caret index position to be 14. The problem is that the found methods that use a treeWalker do not work in this case. The bold tag is not found... probably because it isn't closed. Also I have tried several methods but still no luck.

I need it to work in Firefox. Thank you.

like image 811
helldrain Avatar asked May 24 '13 13:05

helldrain


People also ask

How do you set a caret cursor position in Contenteditable element div?

In order to set caret cursor position in content editable elements like div tag is carried over by JavaScript Range interface. The range is created using document. createRange() method.

How do I set the cursor position in input?

Use the setSelectionRange() method to set the current text selection position to the end of the input field. Call the focus() method on the input element. The focus method will move the cursor to the end of the input element's value.

What is caret position in JavaScript?

The CaretPosition interface represents the caret position, an indicator for the text insertion point. You can get a CaretPosition using the Document. caretPositionFromPoint() method.


2 Answers

Have you tried this? Get a range's start and end offset's relative to its parent container

Direct link to the jsfiddle: https://jsfiddle.net/TjXEG/1/

Function code:

function getCaretCharacterOffsetWithin(element) {
    var caretOffset = 0;
    if (typeof window.getSelection != "undefined") {
        var range = window.getSelection().getRangeAt(0);
        var preCaretRange = range.cloneRange();
        preCaretRange.selectNodeContents(element);
        preCaretRange.setEnd(range.endContainer, range.endOffset);
        caretOffset = preCaretRange.toString().length;
    } else if (typeof document.selection != "undefined" && document.selection.type != "Control") {
        var textRange = document.selection.createRange();
        var preCaretTextRange = document.body.createTextRange();
        preCaretTextRange.moveToElementText(element);
        preCaretTextRange.setEndPoint("EndToEnd", textRange);
        caretOffset = preCaretTextRange.text.length;
    }
    return caretOffset;
}

function showCaretPos() {
    var el = document.getElementById("test");
    var caretPosEl = document.getElementById("caretPos");
    caretPosEl.innerHTML = "Caret position: " + getCaretCharacterOffsetWithin(el);
}

document.body.onkeyup = showCaretPos;
document.body.onmouseup = showCaretPos;
like image 56
Eric McCormick Avatar answered Sep 21 '22 01:09

Eric McCormick


just had to do this so there is some working solution (some testing may be required)

basic idea is to:

  1. get textContent position using this method: Get caret (cursor) position in contentEditable area containing HTML content

  2. iterate through innerHTML of an element to the textContent position

  3. if html tag or entity is encountered, iterate through it until normal char, then continue

sample code here:

function getCaretPosition (node) {
    var range = window.getSelection().getRangeAt(0),
        preCaretRange = range.cloneRange(),
        caretPosition,
        tmp = document.createElement("div");

    preCaretRange.selectNodeContents(node);
    preCaretRange.setEnd(range.endContainer, range.endOffset);
    tmp.appendChild(preCaretRange.cloneContents());
    caretPosition = tmp.innerHTML.length;
    return caretPosition;
}

function getHTMLCaretPosition(element) {
var textPosition = getCaretPosition(element),
    htmlContent = element.innerHTML,
    textIndex = 0,
    htmlIndex = 0,
    insideHtml = false,
    htmlBeginChars = ['&', '<'],
    htmlEndChars = [';', '>'];


if (textPosition == 0) {
  return 0;
}

while(textIndex < textPosition) {

  htmlIndex++;

  // check if next character is html and if it is, iterate with htmlIndex to the next non-html character
  while(htmlBeginChars.indexOf(htmlContent.charAt(htmlIndex)) > -1) {
    // console.log('encountered HTML');
    // now iterate to the ending char
    insideHtml = true;

    while(insideHtml) {
      if (htmlEndChars.indexOf(htmlContent.charAt(htmlIndex)) > -1) {
        if (htmlContent.charAt(htmlIndex) == ';') {
          htmlIndex--; // entity is char itself
        }
        // console.log('encountered end of HTML');
        insideHtml = false;
      }
      htmlIndex++;
    }
  }
  textIndex++;
}

//console.log(htmlIndex);
//console.log(textPosition);
// in htmlIndex is caret position inside html
return htmlIndex;
}
like image 31
YangombiUmpakati Avatar answered Sep 20 '22 01:09

YangombiUmpakati