4

I need to get the text (or the whole inner html) of a node, truncated on current caret (text cursor) position in an element with contenteditable set to true. I've tried using range.setStart() etc, but I can't make heads and tails of it...

Edit: For clarification, upon some events I want the script to extract text from the beginning of the node that currently has focus to the caret's position (where the blinking vertcal line currently is if an editable field has focus) and store it in a variable. Action similar to what would happen if a user pressed ctrl+shift+home and ctrl+c

Example: Given html:

<span contenteditable=true>Hello, world<br> Good bye, World</span>

And assuming that the caret is between "Good" and "bye", I'd like to retrieve

"Hello, world<br> Good"

Christoph
  • 50,121
  • 21
  • 99
  • 128
Kuba Orlik
  • 3,360
  • 6
  • 34
  • 49
  • Please provide some more details ... – mate64 May 19 '13 at 12:45
  • That's pretty much all there is to it. Upon some events I want to extract text from the beginning of the node that currently has focus to the caret's position and use it later in the script. Action similar to what would happen if a user pressed ctrl+shift+home and ctrl+c – Kuba Orlik May 19 '13 at 13:02
  • What do you mean by caret? What kind of node? – Fresheyeball May 19 '13 at 14:31
  • by "caret" I mean text cursor. It can be any node, I'm thinking about a span tag with contenteditable attribute – Kuba Orlik May 19 '13 at 14:51
  • So post some html, tell us where the caret will be (in this case for this example) and tell us clearly what you expect to retrieve from the function. – David Thomas May 19 '13 at 14:56
  • I've provided an example in the question with the last edit – Kuba Orlik May 19 '13 at 15:42
  • I've edited your question so that it mentions `contenteditable` at the beginning, otherwise it's very unclear what you want to do. – MMM May 19 '13 at 15:44

4 Answers4

7

I would suggest the following approach:

  • create a range encompassing the content you want
  • call the range's cloneContents() method to obtain a DocumentFragment representing the range's content
  • create a <div> or <body> element and append the fragment to it
  • get the element's innerHTML

Example: http://jsfiddle.net/sUSYG/

Code:

function getHtmlPrecedingSelectionIn(container) {
    var html = "";
    if (window.getSelection && document.createRange) {
        var sel = window.getSelection();
        if (sel.rangeCount > 0) {
            var selRange = sel.getRangeAt(0);
            var range = document.createRange();
            range.selectNodeContents(container);
            range.setEnd(selRange.startContainer, selRange.startOffset);

            var frag = range.cloneContents();
            var el = document.createElement("body");
            el.appendChild(frag);
            html = el.innerHTML;
        }
    }
    return html;
}

Caveats:

  • this won't work in IE <= 8, although it's not too hard to do (either by coding something using its different selection/range API or by using a library such as Rangy)
  • the HTML produced is not guaranteed to be a substring of the original HTML of the editable span.
Tim Down
  • 318,141
  • 75
  • 454
  • 536
6

You can do this fairly easily using Rangy and jQuery.

Here's a jsFiddle demonstrating this approach. The comments explain what is happening.

$("contenteditable-element").click(function () {
    // Get the current selection with Rangy
    var sel = rangy.getSelection()
    // Insert a temporary caret element at the caret position 
    // (which is inside the contenteditable element)
    if (sel.rangeCount) sel.getRangeAt(0).insertNode($("<caret />")[0]);
    // Read the html inside the contenteditable element
    var innerHTML = $("contenteditable-element").html();
    // Clean up, get rid of the caret element
    $("caret").remove();
    // Only keep the text before the first occurrence of the caret element
    innerHTML = innerHTML.substr(0, innerHTML.indexOf('<caret>'));
});
Mathijs Flietstra
  • 12,900
  • 3
  • 38
  • 67
0

Maybe this will push you in the right direction: Get a range's start and end offset's relative to its parent container

I built a fiddle, that shows the function at work with your example: http://jsfiddle.net/Kn8qr/2/

function getCaret(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;
}
var elCont=element.innerHTML;

var truncCont=elCont.substr(0,caretOffset);

alert(truncCont);
}

Credit for this goes to the function's author, I just edited it abit.

Community
  • 1
  • 1
bouscher
  • 1,300
  • 1
  • 9
  • 18
0

Have a look at this answer: https://stackoverflow.com/a/16575647/1532004 I think think he is doing something similar. Perhapes he's shooting over the moon, but he got a working solution :)

Community
  • 1
  • 1
Zim84
  • 3,404
  • 2
  • 35
  • 40