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.
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With