22

I have some javascript that manipulates html based on what the user has selected. For real browsers the methods I'm using leverage the "Range" object, obtained as such:

    var sel = window.getSelection();
    var range = sel.getRangeAt(0);
    var content = range.toString();

The content variable contains all the selected text, which works fine. However I'm finding that I cannot detect the newlines in the resulting string. For example:

Selected text is:

abc

def

ghi

range.toString() evaluates to "abcdefghi".

Any search on special characters returns no instance of \n \f \r or even \s. If, however, I write the variable out to an editable control, the line feeds are present again.

Does anyone know what I'm missing?

It may be relevant that these selections and manipulations are on editable divs. The same behaviour is apparent in Chrome, FireFox and Opera. Surprisingly IE needs totally different code anyway, but there aren't any issues there, other than it just being IE.

Many thanks.

Timbo
  • 4,505
  • 2
  • 26
  • 29
  • What do you mean by 'range.toString() evaluates to "abcdefghi"'? What are you using to examine that value? A debugger? alert()? – Ben Dunlap Jul 18 '09 at 04:39
  • Are you writing `range` value to editable control or `range.toString()`? – RaYell Jul 18 '09 at 04:48
  • @Ben Dunlap all of the above, using external debuggers, alerts, or browser based debuggers. For instance hitting into a breakpoint with Visual Studio. @RaYell - I tried writing the content resulting from my range.toString() call into an editable div, to prove it maintained the newline information. 1st answer nailed this though, the sel.toString() has the newlines, range.toString() doesn't. Thanks all. – Timbo Jul 18 '09 at 10:43

3 Answers3

18

Editing my post:

Experimenting a bit, I find that sel.toString() returns new lines in contenteditable divs, while range.toString() returns newlines correctly in normal non-editable divs, but not in editable ones, as you reported.

Could not find any explanation for the behaviour though.

This is a useful link http://www.quirksmode.org/dom/range_intro.html

Teun Zengerink
  • 4,277
  • 5
  • 30
  • 32
Narayan Raman
  • 891
  • 6
  • 9
  • Excellent, yep, sel.toString() has the newlines. Thanks very much! – Timbo Jul 18 '09 at 10:37
  • 2
    The explanation is simple and is laid out in the DOM Range spec: `Range.toString()` only includes content from text nodes. `
    ` elements and line breaks implied by block elements are therefore not included.
    – Tim Down Aug 14 '14 at 08:41
  • 2
    `document.getSelection().toString()` is not working in IE 11. its merging all new line text into one. – Aman Gupta Apr 01 '16 at 14:44
  • You are the best . This is one of the uggliest displays of JS horrors I've ever seen. I had being seeking for a way to get the selected text (including newlines) for hours until now. Thank you very much!!! – Gabo Alvarez Jul 22 '18 at 00:07
4

I found at least two other ways, so you may still use the range to find the position of the caret in Mozilla.

One way is to call

var documentFragment = rangeObj.cloneContents ();

which holds an array of childNodes, and any line breaks will show as a node of class "HTMLBRElement".


The other way is to make sure every "br" tag is followed by a newline character (0x0a)!

This won't hurt the HTML content in any visible way, but now all HTML breaks are translated to plain text line breaks as soon as range.toString() is being called!


I hope this helps - even if this topic is very old. (I'm a necromancer anyway already, hehe) :)

  • Using something like `[].slice.call(window.getSelection().getRangeAt(0)).reduce(...)` and replacing `HTMLBRElement` nodes with line breaks is the only way I've been able to get IE11 to copy newlines. – Morgan Delaney May 26 '18 at 02:21
0

Thanks to the OP I was able to do it using window.getSelection() as he suggested. I needed to get the text until the caret position on an InputEvent , which gives me a static range with the inserted text. So I have a range but not necessarily the current selection's range.

function richToPoorText(range){
    //Caso base, está colapsado.
        console.log(range);
        var restoreRange=document.createRange(); //needed for restoring the caret pos.
        restoreRange.setStart(range[0].endContainer, range[0].endOffset);
        restoreRange.setEnd(range[0].endContainer, range[0].endOffset);
        rangeClone=document.createRange();
        rangeClone.setStart(__baseEditor,0);
        rangeClone.setEnd(range[0].endContainer, range[0].endOffset);
        var str;
        var sel=window.getSelection();
        sel.removeAllRanges();
        sel.addRange(rangeClone);   //sel does converts the br to newlines
        str=sel.toString();
        sel.removeAllRanges();
        sel.addRange(restoreRange);
        return str;

}

Thankyou very much OP. And if someone has the same use case, you can use this snipset

Edit: __baseEditor is a global variable pointing to the editor's main contenteditable div

Gabo Alvarez
  • 139
  • 8