In a content-editable div, I want to surround selected text with a <span>
, or modify an existing <span>
when I select all the text within it. I can do the first, but not the second.
In the second case, if I have A<span>B</span>C
and I select B, the selection shows the anchorNode
as A
, the focusNode
as C
, and toString()
returns B
. The parent node is the enclosing paragraph, not the <span>
. I cannot find any way to distinguish between selecting B
in A<span>B</span>C
and in ABC
, since I can't find any way to discover the existence of the <span>
element that surrounds B
in the first case. There must be a way to do this, surely? Can someone tell me how to do this?
Here is the code:
var sel = window.getSelection();
var range = sel.getRangeAt(0);
var selected = range.compareBoundaryPoints(range.START_TO_END, range);
if (selected > 0) { // some text is selected
var el = range.commonAncestorContainer;
//
// would expect to find el == <span> if selection is B
// in <p>A<span>B</span>C<p>, but find <p> instead!
//
//... code to surround selection with new span or to process
// existing span... but there is never an existing span!
}
I've come up with a rather horrible workaround for the above problem. To test if the selection is the whole of a <span>
element, I clone the range, which, when it is the whole span, contains the span preceded and followed by empty strings. So what I do is to remove any of these, and check if what I'm left with is a single span element. If that's the case, I take the next element after the start of the original range, which will be the <span>
:
var el = range.commonAncestorContainer;
var copy = range.cloneContents();
while (copy.childNodes.length > 1 && copy.firstChild.nodeValue === "") {
copy.removeChild(copy.firstChild);
}
while (copy.childNodes.length > 1 && copy.lastChild.nodeValue === "") {
copy.removeChild(copy.lastChild);
}
if (copy.childNodes.length == 1 && copy.childNodes[0] instanceof HTMLSpanElement) {
el = range.startContainer.nextSibling;
}
This works, but it's not very appealing. I'd be happy if anyone has a neater solution.