9

Here is what I am trying to accomplish: When a user uses a mouse, keyboard, or touch to select text inside "myDiv" I want to acquire three discreet chunks of HTML: the HTML before the selection (to the "left" of it), the HTML inside the selection, and the HTML after the selection (to the "right" of it). The html should be as it would appear with myDiv.innerHTML.

The selection might start or end inside a tag pair (i.e., the isolated selection isn't necessarily valid HTML). I don't need to deal with special scenarios like absolute-positioned elements within the selection; all of the selections I am concerned with will be constrained to one div that will contain basic tags like strong, em, ul, ol, h1, image, and table.

The closest I've come is using rangy to snag the selection and calling selection.getRangeAt(0).cloneContents() to get the selection HTML. This works well enough until I make a selection that is invalid in isolation, and the browser alters the HTML of the document fragment to make it valid markup.

Extra Information: Here's why I need this:

I am creating a document feedback system, so I need to save the selection information to a database for later retrieval and reconstitution. Normally I would save the selection using the DOM path and the selected text, but the text may change between saving and reconstitution. For example, the author might move entire paragraphs around, delete sections, etc. The DOM path becomes pretty useless then.

So my (imperfect) plan is to store the selection as [offset, length, html_snippet]. That's the "position". I'll also store the html snippets that came directly before and after the selected text. This is the "context".

Using a combination of these data I should be able to relocate the originally selected text most of the time, even if it has moved or partially changed. When that fails, the UI will have a way to address it, but I'd like that to occur as infrequently as possible.

Superthanks!

Alan Bellows
  • 1,781
  • 1
  • 14
  • 21
  • Yeah, I was trying to provide as much info as possible, and instead I was obnoxiously verbose. Just now I whittled it down a bit. :) – Alan Bellows Mar 02 '12 at 19:18
  • Just to be clear: are the HTML snippets you need required to be exactly as they appear in the HTML source sent to the browser? – Tim Down Mar 03 '12 at 15:21
  • @TimDown All of this is occurring inside a div. With respect to that div, I need either the exact html as it was sent to the browser, or the browser-normalized html I'd get with myDiv.innerHTML. I can work with either, as long as there's no insertion of new tags. – Alan Bellows Mar 03 '12 at 17:58
  • I'm not hopeful about achieving this. It would require nasty code to parse HTML strings. Could you not put hidden markers into your HTML to denote selection boundaries instead? – Tim Down Mar 03 '12 at 18:51

2 Answers2

1

I have several questions:

1.- When you say 'the html after the selection' - how would that html be any different than the html previous to the selection or viceversa? Is the 'selection' process itself tampering with the html because of your 'script' or whatever?

2.- You said the text selections are not taking place in textareas...what elements are you working with then? paragraphs? divs...? Narrowing it down would help.

3.- Have you thought about using jquery?

http://api.jquery.com/select/

Doing something like

$('#element_with_text_goes_here').select(function() {

//apply grabbing functions here, for example

//copy html 'before' selection:
     $pre_html = $('html').clone();

   // copy selection...see below:

   // copy html 'after' selection'...same as before


});

Copy selection:

As noted here:

Selecting text in an element (akin to highlighting with your mouse)

Jason wrote the following function:

function selectText(element) {
    var doc = document;
    var text = doc.getElementById(element);    

    if (doc.body.createTextRange) { // ms
        var range = doc.body.createTextRange();
        range.moveToElementText(text);
        range.select();
    } else if (window.getSelection) { // moz, opera, webkit
        var selection = window.getSelection();            
        var range = doc.createRange();
        range.selectNodeContents(text);
        selection.removeAllRanges();
        selection.addRange(range);
    }
}

With a live working demo that can be found here: http://jsfiddle.net/edelman/KcX6A/339/

And a jquery plugin version here: http://jsfiddle.net/edelman/KcX6A/340/

Which you can use for the obtention of the selected text. You'll just have to tweak it accordingly since he was approaching it from a reversed angle. The more details you can give us...the better we can help.

Hope this helps
G

Community
  • 1
  • 1
Capagris
  • 3,811
  • 5
  • 30
  • 44
  • In response to your first question: By "previous" to the selection I don't mean "in the past," I mean "on the left of". Imagine that making a selection divides the contents of my div into three sections: html on the left of the selection, html inside the selection, and html to the right of the selection. I need to get the unmodified html for each of those sections. – Alan Bellows Mar 03 '12 at 18:04
  • In response to your second question: This is all contained in a div which contains some tags such as strong, em, image, etc. – Alan Bellows Mar 03 '12 at 18:05
  • ...and jQuery is definitely my preference, but not required. – Alan Bellows Mar 03 '12 at 18:06
0

This code gets html/text from user's selection, but it works in IE only. The code works with cross-tag selection too. (Globals used to keep the code short.)

<script>
function selected(){
    thediv=document.getElementById('div');
    res=document.getElementById('htm');
    userSelection=document.selection;
    userRange=userSelection.createRange();
    /* For wider scale of elements */
    // rangeParent=userRange.parentElement();
    // if(rangeParent!=thediv) userRange.moveToElementText(rangeParent);
    rangeText=userRange.htmlText;   // OR: rangeText=userRange.text;
    res.innerText=rangeText;    
    return; 
}    
</script>
</head>    
<body onload="document.onselectionchange=selected;">
<div id="div">
<h1>The great testpage</h1>
<p>A paragraph with some text</p>
<p>This paragraph <b>contains</b> a child element.</p>
<p>And this is the last paragraph.</p>
<table>
<tr><td>Cell1-1</td><td>cell1-2</td></tr>
<tr><td>Cell2-1</td><td>cell2-2</td></tr>
</table>
<ol>
<li>item1</li>
<li>item2</li>
<li>item3</li>
</ol>
</div>
<br>
<span id="htm"></span>
</body>

Content before&after selection in the thediv you'll get like this: prepost=thediv.innerHTML/innerText.split(rangeText);

If the page contains any other elements but thediv, they have to be made unselectable.

Teemu
  • 22,918
  • 7
  • 53
  • 106
  • IE-only isn't workable for me, but thanks for the effort regardless. :) – Alan Bellows Mar 03 '12 at 18:00
  • @AlanBellows Before you "whittled" your original post, IE was mentioned. Anyway, the code contains certain ideas, which possible could be "translated" for other browsers too. This answer was not even purposed to be a perfect solution to your question. You've very interesting and challenging task, good luck with it... – Teemu Mar 03 '12 at 18:13
  • In my original post it said it's okay if it _doesn't_ work in IE. :) What I seek may not even be possible, but by gum I'm gonna try. Thanks! – Alan Bellows Mar 03 '12 at 18:29