I have a web-application where the user can create visualization elements within a div.
In one example the user created a div that’s clickable and has a hover-event, but is a sibling to, and displayed on top of, elements that have context-menu events on them.
Is it possible to have the element on top (element with class me
in the snippet) respect on-click events while still transparently forwarding context-menu events to the elements that appear under the mouse cursor (elements with class cousin
in the snippet), even though they are siblings?
I’ve tried capturing context-menu events in the editor
div, finding all direct children that are under the mouse-cursor and a dispatching a new context-menu event on them, but since the elements that I want to receive the events are nested within the sibling, this event doesn’t reach them.
let editor = document.getElementById("editor");
editor.oncontextmenu = e => {
// Skip opening the browser context-menu.
e.preventDefault();
for (let i = 0; i < editor.children.length; i++) {
let currentChild = editor.children[i];
if (currentChild == e.target) {
// Avoid executing the event on the element that received it.
continue;
}
let currentRect = currentChild.getBoundingClientRect();
if (currentRect.left < e.offsetX &&
currentRect.top < e.offsetY &&
currentRect.right > e.offsetX &&
currentRect.bottom > e.offsetY) {
// Problem here is that the new event is only sent to the sibling, not further down to the texts.
// Since the text boxes have context menu event, not the sibling, this won't do anything.
// I would preferrably like to just re-execute the right-click while the browser calculates the destination.
// The current way requires every layer to have this custom logic in 'oncontextmenu',
// which is not possible in my scenario.
let ev = new MouseEvent('contextmenu', {
screenX: e.screenX,
screenY: e.screenY,
clientX: e.clientX,
clientY: e.clientY,
ctrlKey: e.ctrlKey,
shiftKey: e.shiftKey,
altKey: e.altKey,
metaKey: e.metaKey,
button: e.button,
});
currentChild.dispatchEvent(ev);
}
}
}
let header = document.getElementById("header");
document.onmousemove = e => {
header.innerHTML = 'Header text<br/>x: ' + e.pageX + '<br/>y: ' + e.pageY;
}
#header {
padding: 25px;
border: 1px solid black;
}
#editor {
position: relative;
}
.sibling {
position: absolute;
background-color: red;
border: 5px solid blue;
opacity: 0.3;
width: 250px;
height: 250px;
top: 50px;
left: 75px;
}
.me {
position: absolute;
opacity: 0.3;
width: 300px;
height: 300px;
top: 25px;
left: 50px;
border: 5px solid black;
}
.me:hover {
border-color: red;
background-color: green;
}
.nephew {
height: calc(100% - 30px);
width: calc(100% - 30px);
margin: 9px;
padding: 5px;
border: 1px solid white;
}
.cousin {
color: yellow;
align-content: center;
text-align: center;
margin: auto 0;
height: 50%;
width: 100%;
border: 1px solid blue;
}
<header id="header">Header text<br/> x: ?<br/> y: ?</header>
<div id="editor">
<div class="sibling">
<div class="nephew">
<div class="cousin">
<div class="text" oncontextmenu="alert('context-menu on 'Value 1'')">Value 1</div>
</div>
<div class="cousin">
<div class="text" oncontextmenu="alert('context-menu on 'Value 2'')">Value 2</div>
</div>
</div>
</div>
<div class="me" onclick="alert('clicked 'me'')" oncontextmenu="alert('context-menu on 'me'')"></div>
</div>
Same snippet in JSFiddle:
https://jsfiddle.net/7gnqsm51/9/
The project does not currently use JQuery, so I would prefer solutions without it.
2