I have several svg use
elements within an svg
element, with event handlers for touchstart/move/end
attached to the svg
element. When a user touches the svg use
element on the screen and moves their finger around, the element moves around the screen as desired.
//Exact code in fiddle is different, this is example
<svg id="parent" viewBox="0 0 1000 1000">
<use href="test.svg" width="100" height="100" x="100" y="100">
<use href="test2.svg" width="100" height="100" x="100" y="100">
</svg>
//Exact code in fiddle is different, this is example
let parentSVG = document.getElementById("parent");
parentSVG.addEventListener('touchstart', Handle_DragStart); //mousedown too
parentSVG.addEventListener('touchmove', Handle_Drag); //mousemove too
parentSVG.addEventListener('touchend', Handle_DragEnd); //mouseup too
The problem is that I want the dragged element to always be drawn on top of the others, and the elements are drawn in DOM-order (first element in DOM first). To handle this, in my touchstart
event handler I'm moving the element the user touched to the end of the list via appendChild()
:
//Exact code in fiddle is different, this is example
let mid_move = false;
function Handle_DragStart (event)
{
//Needed to ignore other touch events while dragging
if(mid_move)
return;
mid_move = true;
//The event.target is the svg use element.
//It is already attached to the parentSVG.
//This just moves it to the end of the list of children.
parentSVG.appendChild(event.target);
}
This works like a charm for mousedown
and mousemove
but not touchstart
and touchmove
. After doing this the touchmove
event doesn't fire until I touch the screen a second time, presumably because the appendChild()
call in the touchstart
handler doesn't change anything at that point?
Am I invalidating the event? How should I handle this situation?
EDIT:
JSFiddle example. //Set append_child to true/false to see behavior
I circumvented the problem with a modified form of @JanStránský suggestion. I placed a transparent svg rectangle covering the entire drag area to act as a drag surface, and listed this at the end of the list of svg elements.
Then when handling
touchstart
, I used the mouse position to determine which svg element underneath this clear svg rectangle the user was selecting. I then calledparentSVG.insertBefore(selected, clear_rect)
so that the element was drawn above everything else but beneath this drag surface (so subsequent drags would work).The draggable elements are all laid out in (and snapped-to) a grid, so mouse position -> selected element is easily determined. Otherwise you may need one clear rect per draggable element as suggested.
This solution seems to prevent me from using CSS features
cursor: grab;
when over the element since the large drag surface is in the way. Maybe I'll just implement it manually.