I need to manually construct/fire a mousedown event in a way that can be handled by any relevant event handlers (either in straight JS, jQuery, or interact.js), just as a natural mousedown event would. However, the event does not seem to trigger anything the way I expect it to.
I am trying to make some irregularly shaped images draggable using the interact.js library. The images are simply rectangular img elements with transparent portions. On each element, I have defined 2 interact.js event listeners:
However, if the img elements are overlapping, and the user clicks in the transparent area of the top element but on the filled area of the lower element, the lower element should be the target of the drag. After trying several things (see below), I have settled on the solution of: re-ordering the z-indexes of the elements at the same time that I disable drag in step 1, then re-firing the mousedown event on all lower elements. I'm using the "native" event type (not jQuery or interact.js) in hopes that it will literally just replicate the original mousedown event.
// code to re-assign "zIndex"s
function demote(element, interactible, event){
// block dragging on element
interactible.draggable(false);
// get all images lower than the target
var z = element.css("zIndex");
var images = $("img").filter(function() {
return Number($(this).css("zIndex")) < z;
});
// push the target to the back
element.css("zIndex",1);
// re-process all lower events
$(images).each( function () {
// move element up
$(this).css("zIndex",Number($(this).css("zIndex"))+1);
// re-fire event if element began below current target
elem = document.getElementById($(this).attr('id'));
// Create the event.
e = new MouseEvent("mousedown", {clientX: event.pageX, clientY: event.pageY});
var cancelled = !elem.dispatchEvent(e);
});
}
Unfortunately, this does not work, as the mousedown event does not register with any of the handlers. Why?
I have all the (relevant) code at this JSFiddle: https://jsfiddle.net/tfollo/xr27938a/10/ Note that this JSFiddle does not seem to work as smoothly as it does in a normal browser window, but I think it demonstrates the intended functionality.
Other things I have tried:
Lots of people have proposed different schemes to handle similar problems (i.e. forwarding events to lower layers, using pointer-events: none, etc), but none seem to work to trigger the interact.js handler and start a drag interaction on the right element. I also tried using interaction.start (provided by interact.js) but it seems buggy--there is at least one open issue on the topic and when I tried to start a new drag interaction on a target of my choice I got lots of errors from within the library's code.
I'm not against revisiting any of these solutions per se, but I would also really like to know why manually firing a mousedown event won't work.
The idea is to listen down
event on parent element and to choose manually drag target. Also I didn't use z-index for choosing which image to drag, because z-index doesn't work with position:static
. Instead of that I just gave priorities to images, it's all up to you. https://jsfiddle.net/yevt/6wb5oxx3/3/
var dragTarget;
function dragMoveListener (event) {
var target = event.target,
// keep the dragged position in the data-x/data-y attributes
x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx,
y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
// translate the element
target.style.webkitTransform =
target.style.transform =
'translate(' + x + 'px, ' + y + 'px)';
// update the posiion attributes
target.setAttribute('data-x', x);
target.setAttribute('data-y', y);
}
function setDragTarget(event) {
//choose element to drag
dragTarget = $('#parent').find('img')
.filter(function(i, el) {
var clickCandicateRect = el.getBoundingClientRect();
return insideBoundingRect(event.x, event.y, clickCandicateRect);
}).sort(byPriority).get(0);
}
function insideBoundingRect(x, y, rect) {
return (rect.left <= x) && (rect.top <= y) && (rect.right >= x) && (rect.bottom >= y);
}
function byPriority (a, b) {
return $(b).data('priority') - $(a).data('priority');
}
function startDrag(event) {
var interaction = event.interaction;
var target = dragTarget;
if (!interaction.interacting()) {
interaction.start({ name: 'drag' }, event.interactable, target);
}
}
//Give priority as you wish
$('#face1').data('priority', 2);
$('#face2').data('priority', 1);
interact('#parent').draggable({
//use manualStart to determine which element to drag
manualStart: true,
onmove: dragMoveListener,
restrict: {
restriction: "parent",
endOnly: true,
elementRect: { top: 0, left: 0, bottom: 1, right: 1 }
},
})
.on('down', setDragTarget)
.on('move', startDrag);
Have you tried to set the css property pointer-events: none
on the higher levels (it could also be set via javascript)?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With