Reputation: 83
So I have this problem where I need to get the clicked spot offset relative to the parent element. Lets assume I have html rendered like that:
<div>
able to allocate many IPs.
<span style="background: yellow">
Proof-of-work is essentially one-CPU-one-vote.
</span> The majority
</div>
I want to get the offset of click relative to the parent div. For example if I click the word is in span tags I get the offset of the click relative to the parent Div. How can I implement this. I have tried to write code like this:
var selection = window.getSelection();
var range = selection.getRangeAt(0);
var offset = range.startOffset;
But this gives me the offset relative to the span tag. So how can I get the offset relative to the div tag?
Upvotes: 3
Views: 1526
Reputation: 25855
I am not aware of any built-in functions that can measure the offset of a Range relative to another element. You can use the Range's startContainer
property and then crawl up and across the DOM hierarchy, measuring the content as you go along.
I created a demo below. Note, however, that this may or may not produce exactly the result you are looking for. For example, this will measure the length of comments and style nodes, and it will count all the space characters, whether or not the browser ultimately renders them. You'll probably need to make adjustments to suit your own purposes by adding checks for the nodeType before measuring, pre-collapsing space characters (perhaps using regex replace), etc, but I hope this is a good starting place.
function clicked(){
console.log( getSelectionOffsetRelativeTo( document.getElementById( 'parent' ) ) );
}
function getSelectionOffsetRelativeTo(parentElement, currentNode){
var currentSelection, currentRange,
offset = 0,
prevSibling,
nodeContent;
if (!currentNode){
currentSelection = window.getSelection();
currentRange = currentSelection.getRangeAt(0);
currentNode = currentRange.startContainer;
offset += currentRange.startOffset;
}
if (currentNode === parentElement){
return offset;
}
if (!parentElement.contains(currentNode)){
return -1;
}
while ( prevSibling = (prevSibling || currentNode).previousSibling ){
nodeContent = prevSibling.innerText || prevSibling.nodeValue || "";
offset += nodeContent.length;
}
return offset + getSelectionOffsetRelativeTo( parentElement, currentNode.parentNode );
}
Click inside the div:
<div id="parent" style="border: black 1px solid;" onclick="javascript:clicked();">
Currently in the parent node.
<span style="color: red;">In the first child span.</span>
Back in the parent node.
<span style="color: red;">In the second child span.
<span style="font-style:italic;">In the grandchild span.</span>
</span>
Back in the parent node.
</div>
Upvotes: 3