5

Let's say I have a contentEditable div, to the user can edit and change the text and elements inside it. How do I arbitrarily change the selection in this div with javascript? By "change" I don't mean "change the contents of whatever the user has selected", I mean actually change what is selected. The user should then be able to type over the selection, replacing it with something else.

This should take into account that I may want to select text across elements. For instance:

<p>Some text <span>goes</span> here.</p>

I may for instance want to select "Some text go", or everything inside the <p>.

This only needs to work in Safari/Webkit.

Thanks in advance. As a native code developer, I find the DOM and Javascript in general quite frustrating.

Lucas
  • 6,328
  • 8
  • 37
  • 49

4 Answers4

12

Just to answer my own question in detail so anyone searching for something similar doesn't have to go looking elsewhere...

The code I ended up using was something like this:

var range = document.createRange();
range.setStart( <get the node the selection starts in>, <char offset in that node> );
range.setEnd( <get the node the selection ends in>, <char offset in that node> ); 

window.getSelection().removeAllRanges();
window.getSelection().addRange(range);

Big thanks to James Black for pointing me in the right direction.

Lucas
  • 6,328
  • 8
  • 37
  • 49
  • Thanks for posting it here :) – Tomáš Zato Jan 22 '14 at 21:08
  • Best SO user ever: adding by your own the final implementation that solves your question, but checking as "accepted answer" that one that helped you find the right solution. Respect – Ma3x Feb 07 '23 at 10:57
2

You can use document.getElementById('your_text_id').setSelectionRange(start, end); and you can use Math.random() to generate random numbers for start and end

Soufiane Hassou
  • 17,257
  • 2
  • 39
  • 75
  • Thanks, but I need to be able to select across element boundaries too. And by "arbitrary", I didn't mean *random*. I know exactly the elements where the selection should start and end, down to the character offset. – Lucas Nov 15 '09 at 17:38
2

Unless you need to write your own, you may want to look at tinyMCE, http://tinymce.moxiecode.com/, as it is a nice WYSIWYG editor in javascript.

In order to do this you will probably want to look at something like this: http://codingtricks.blogspot.com/2009/03/javascript-select-partial-text-in-div.html

These may also be helpful: JavaScript ranging gone wrong https://developer.mozilla.org/en/DOM/window.getSelection

What you are trying to do will be complex, as you will need to take the selected area, remove all the tags, then put in the tag that you want for the selected area.

Community
  • 1
  • 1
James Black
  • 41,583
  • 10
  • 86
  • 166
0

While @Lucas's answer is good, there is a lot missing that would allow you to successfully use this. The node the selection starts in has to be the exact node, not a parent. In our case we were trying to put some text into a TextAngular control, then select text that looked liked ____ so the user could "fill in the blank".

Our input was html of the order <p>Some text goes here: _____</p> or

<p>Some partial goes here
<ul>
    <li>Option 1</li>
    <li>_____</li>
</ul>

To get this to work, we had to write something to find the underscores in the right element

function find_(node) {
    var i, n;
    // return the node with the _'s
    for(i=0; i < node.childNodes.length; ++i) {
        n = node.childNodes[i];
        console.debug(n);
        if(n.textContent) {
            console.debug(n, n.textContent);
            if(n.textContent.search(/___+/) > 0) {
                return n;
            }
        }
        if(n.childNodes) {
            console.debug(n, n.childNodes);
            n = find_(n);
            if(n) {
                return n;
            }
        }
    }
    return null;
}

So in the end, finding the node to satisfy <get the node the selection starts in> was a lot more work than that simple sentence led me to believe.

In the <ul> case. the node that contains the ____ is firstChild of the li node.

I've put this here to help others that need to do this not wonder why they are getting the error message

IndexSizeError: Failed to execute 'setStart' on 'Range': There is no child at offset 65.

When the problem is they are just looking at the wrong node.

boatcoder
  • 17,525
  • 18
  • 114
  • 178