Detect mouse right click outside of custom selection (highlight)
After many tries *, I think there is no clean solution.
I finally came up with a jstonne answer tag (which itself is MikeB's answer in the same thread) to something that might suit your needs.
It involves getting the Range
current selection on the right click (easy part), and then checking if the event node's target is in that range (hard part). Unfortunately, there is no easy way to get the nodes contained in the Range object, so we must iterate over the nodes contained between Range.startContainer
and Range.endContainer
.
* even one implying range.extractContents () and MutationObservers
NOTES:
- Since Chrome seems to make a new range selection for us when we do a right click, it will always return true if you right click on the text.
- When you select a tag
img
, you are actually selecting the textNodes before and after the element, so if you select the first image in the example below and you right click on the textSome Dummy Text
it will return. But if you do the same test on the second image, it won't happen as the text is wrapped in an element<span>
. - There can be many other caveats (depending on the browser, os, etc.),
function isInsideSelection(event){ event.preventDefault(); var p = document.querySelector('#result'); p.innerHTML= '' var sel = window.getSelection(); if(!sel.rangeCount) return; var range = sel.getRangeAt(0); // nothing selected : don't do anything if(range.collapsed) return; // Firefox allows us to get the real node we clicked on var result = isTargetInRange(range, event.explicitOriginalTarget, event.target); p.innerHTML= 'clicked in the target: '+result; } function getNextNode(node) { if (node.firstChild) return node.firstChild; while (node) { if (node.nextSibling) return node.nextSibling; node = node.parentNode; } } function isTargetInRange(range, nodeTarget, elementTarget) { var start = range.startContainer.childNodes[range.startOffset] || range.startContainer; var end = range.endContainer.childNodes[range.endOffset] || range.endContainer; var commonAncestor = range.commonAncestorContainer; var nodes = []; var node; var result=false; for (node = start.parentNode; node; node = node.parentNode) { // our target is an element if(!nodeTarget && elementTarget === node ){ result=true; break; } nodes.push(node); if (node == commonAncestor) break; } nodes.reverse(); // walk children and siblings from start until end is found for (node = start; node; node = getNextNode(node)) { // our target can be a textNode if((nodeTarget && nodeTarget === node) || (!nodeTarget && elementTarget === node)){ result=true; break; } if (node == end) break; } return result; }; document.addEventListener('contextmenu', isInsideSelection, false);
#result{position: fixed; bottom:0;right:0}
<p id="result"></p> <img src="http://lorempixel.com/50/50"/> Some Dummy Text <p>Some other text</p> <div><img src="http://lorempixel.com/60/60"/><span>with</span> some text</div>
source to share
Hmm, I don't know a direct answer, but you can try this:
1- in the first click get x1 = clientX and y1 = clientY
2- on mouseup get x2 = clientX and y2 = ClientY
3- right click, check if there is client X and clientY, what we get in 1- and 2- (rectangle with corners x1, y1, x2, y2)
code:
var x1,x2,y1,y2;
var selected = false;
document.onmousedown = function(e){
...
}
document.onmouseup = function(e){
x2 = e.clientX;
y2 = e.clientY;
selected = true;
}
source to share