I'm trying to get a character offsets for the start and end of a selection within an article. However, I would like some elements to be ignored from this process.
In my application, the server is working with the HTML before the JS injects a few elements dynamically and it's quite important that the number of characters in the text remains consistent between the server and client.
I hoped it would be as simple as window.getSelection()
along with user-select: none;
. Sadly, although the text doesn't look selected it's still included in the selection's range.
I've written a short example below. I've had a shot at writing a removeFromSelection
as a workaround without much success. Maybe I need to remove ranges that overlap .unselectable
and manually fill in the gaps with brand new range objects. I'm getting the feeling this should be easier than I'm making it. How should I be doing it?
function findAncestorOffset(container, node, offset)
{
if (node == container)
return offset;
var parent = node.parentNode;
var syblings = parent.childNodes;
for (var i = 0, len = syblings.length; i < len; i++)
{
if (syblings[i] == node)
break;
offset += $(syblings[i]).text().length;
}
return findAncestorOffset(container, parent, offset);
}
function removeFromSelection(selector, selection)
{
$(selector).each(function(i, node){
var range = document.createRange();
range.selectNodeContents(node);
selection.removeAllRanges(range);
});
}
var onSelect = function(){
var sel = window.getSelection();
//removeFromSelection('.unselectable', sel);
var text = sel.toString();
$('#out').text(text);
var range = sel.getRangeAt(0).cloneRange();
var i = findAncestorOffset($('.article')[0], range.startContainer, range.startOffset);
$('#from').text(i);
$('#to').text(i + text.length);
}
$('.article').mouseup(onSelect);
$('.article').focusout(onSelect);
.unselectable {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
pre, .article {
border: solid 1px black;
padding: 12px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1>Select This</h1>
<div class="article">
<p>
This can be selected.
</p>
<p class="unselectable">
This can't.
</p>
<p>
And this can again.
</p>
</div>
<h1>Output</h1>
<div>
From: <span id="from"></span>, To: <span id="to"></span>
</div>
<pre id="out">
</pre>