The key challenge here is that the mouse events are essentially absorbed by the circle. You could perhaps order the elements in such a manner that the a parent g of the arcs holds a child g with the circles to allow events to propagate up to a relevant parent, but then your circles are underneath your arcs.
One option I'll demonstrate below is to strip pointer interaction from the node during the drag. This won't interrupt the drag (tested on Chrome, Firefox, IE), but will allow the mouse to interact with other elements for mouse over / mouse out events, for example. Then we can listen for the mouse to enter (and exit) a target shape, if the drag ends (mouse up) on a target shape we can trigger a specific action.
This requires all event logic for mouse-up to be located in the drag end function, but requires listeners for mouse enter/exit for the target shape(s).
Based on the above, in the below snippet I:
- Strip pointer events from the dragged node (circles) on drag.
- Listen if the mouse moves over a target shape (rectangles) and update that element's datum to reflect its "active" status. Conversely, if the mouse moves off that target shape I set the node to be not active.
- If a drag event ends while a target shape is active, then I know that my dragged node is over the target shape (and mouse up has occurred) and can act accordingly. If the drag event ends with no active node, then I restore pointer events to the node.
In the snippet below I remove nodes (circles) if they are over a target shape (rectangles), updating the rectangles color in the process:
var svg = d3.select("body")
.append("svg")
.attr("width", 500)
.attr("height", 300);
var drag = d3.drag()
.on("drag",dragged)
.on("end", dragEnd);
var squares = svg.selectAll("rect")
.data([{x:30},{x:130},{x:230},{x:330},{x:430}])
.enter()
.append("rect")
.attr("x", function(d) { return d.x; })
.attr("y", 120)
.attr("width", 40)
.attr("height",40)
.attr("fill","white")
.on("mouseenter", function(d) { d.active = true; })
.on("mouseout", function(d) { d.active = false; });
var circles = svg.selectAll("circle")
.data(d3.range(20))
.enter()
.append("circle")
.attr("cx", function(d) { return d/21*500 + 25; })
.attr("cy", function(d) { return d%2 * 40 + 50; })
.attr("r", 10)
.attr("fill", function(d) { return d3.schemeCategory20[d]; })
.call(drag);
function dragged(d) {
var x = d3.mouse(this)[0];
var y = d3.mouse(this)[1];
d3.select(this)
.attr("cx",x)
.attr("cy",y)
.style("pointer-events","none");
}
function dragEnd(d) {
var rect = squares.filter(function(d) {
return d.active;
})
if(!rect.empty()) {
var circle = d3.select(this);
rect.attr("fill",interpolateColor(rect,circle));
circle.remove();
}
else {
d3.select(this).style("pointer-events","all");
}
}
function interpolateColor(r,c) {
return d3.interpolateLab(r.attr("fill"),c.attr("fill"))(0.5)
}
svg {
stroke: black;
stroke-width: 2px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
This checks the mouse position, not if the circle overlaps the rectangle, collision detection would require a different approach