Getting `selectionStart` from a span's innerHTML

innerhtml, javascript, selection-api

Solution

First of all, don't use `innerHTML` for this kind of things. `textContent` is the way to go here.

I'm assuming a standard-compliant browser or IE9+ here.

The first thing to do is to get the document selection. You can do this by

var sel = getSelection();

Now, the selection can be empty, or can be outside the element, so check:

var rng, startSel, endSel, sel;
if (!sel.rangeCount
        || displaytext.compareDocumentPosition((rng = sel.getRangeAt(0)).startContainer) === Node.DOCUMENT_POSITION_PRECEDING
        || displaytext.compareDocumentPosition(rng.endContainer) === Node.DOCUMENT_POSITION_FOLLOWING)
    sel = "";
else {
    ...

At this point, `rng` contains a `Range` object, with the properties `startOffset` and `endOffset`. This values are integers that refers to the position of the `textContent` property of `startContainer` and `endContainer` respectively.

You have a fairly simple case, which is a `<span>` element with a single text node. In this case, `rng.startContainer` and `rng.endContainer` will always be either `displaytext`, or some preceding or following element, but not a descendant of `displaytext`.

    ...
else {
    startSel = displaytext.compareDocumentPosition(rng.startContainer) === Node.DOCUMENT_POSITION_FOLLOWING ? 0 : rng.startOffset;
    endSel = displaytext.compareDocumentPosition(rng.endContainer) === Node.DOCUMENT_POSITION_PRECEDING ? displaytext.textContent.length : rng.endOffset;
    sel = displaytext.textContent.substring(startSel, endSel);
}

In case of a more complex structure of `displaytext`, with children elements and all, things become a little tricky. You'd have to find the text offset of the starting node in the descendants tree of `displaytext`, and the best way to do so is using a `TreeWalker` object walking the text nodes:

var tw = document.createTreeWalker(displaytext, NodeFilter.SHOW_TEXT, null, null);

Problem

I have a roll your own text editor that lets you change portions of a `textarea` element. i want to adapt it to work with a span element. I have no particular attachment to span. The goal is simply to let someone edit html rather than a textarea. I have it working fine in IE but am encountering some problems with Mozilla. Since I'm using a span instead of form input I am using `innerHTML` instead of value. However, I can't seem to get the `selectionStart` and `selectionEnd` functions to work on `innerHTML` as opposed to `value`. Here is the `textarea` code that works fine.... html ``` <textarea id="textarea>Some text goes here</textarea><a href="javascript:void() onclick="editText">edit</a> ``` JS ``` function editText() { var len = displaytext.value.length; var start = displaytext.selectionStart; var end = displaytext.selectionEnd; var sel = displaytext.value.substring(start, end); returns selection ok alert(sel); } ``` However, the following adaption is not limiting the selection to start and end. html ``` <span id="textarea>Some text goes here</span><a href="javascript:void() onclick="editText">edit</a> ``` JS ``` function editText() { var len = displaytext.innerHTML.length; //works ok var start = displaytext.selectionStart; //does not seem to work var end = displaytext.selectionEnd; //does not seem to work var sel = displaytext.innerHTML.substring(start, end); //returns whole innerHTML not selection alert(sel); } ``` Is there a problem with `selecionStart` on `innerHTML`? Workaround? Syntax error? Thanks for any suggesions.

Original source