I want to programmatically fire the "end" event of d3-drag. I have some circles and have the drag-handling of them implemented like so:
...
.call(d3.drag()
.on("drag", function () {...})
.on("end", function () {...})
)
Now, later in my code, I would like to trigger the "end" part of this programmatically.
I have already tried something like this:
d3.select("#myID").dispatch("end");
d3.select("#myID").dispatch("dragend");
d3.select("#myID").call(d3.drag().dispatch("end"));
The DragEvent interface is a DOM event that represents a drag and drop interaction. The user initiates a drag by placing a pointer device (such as a mouse) on the touch surface and then dragging the pointer to a new location (such as another DOM element).
The drag event is fired every few hundred milliseconds as an element or text selection is being dragged by the user.
On the “mouse up” event, the value of drag variable is checked. If the value is true, a “drag” event has occurred and the output is displayed in the console. If the value is 'false' which means there has been no “mouse move” event which also implies that a “click” event had occurred.
If you don't need to generate any actual event data, and I understand the question correctly, you can do this relatively easily without d3.dispatch directly. The below will give you this
and the node data itself (in d3v5 it will also give you i
and nodes
).
D3v5 and earlier
In d3v5 and earlier, the signatures for a function passed to selection.each()
and drag.on()
were the same. In this case you can easily assign the function to a variable and pass it to both. Alternatively, you could access the drag event function with drag.on("typeName")
.
Here's a quick example:
var svg = d3.select("body")
.append("svg")
.attr("width",500)
.attr("height",300);
var data = [{x:40,y:100},{x:250,y:100}];
var circles = svg.selectAll(null)
.data(data)
.enter()
.append("circle")
.attr("r", 10)
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("fill", function(d,i) {
return ["steelblue","crimson"][i]
})
var drag = d3.drag()
.on("drag", function(d) {
d.x = d3.event.x; d.y = d3.event.y;
d3.select(this)
.attr("cx", d.x)
.attr("cy", d.y);
})
.on("end", function(d) {
console.log(d.x+","+d.y);
d3.select(this)
.transition()
.attr("r", 30)
.transition()
.attr("r", 10);
})
circles.call(drag);
d3.select("button").on("click", function() {
var circle = d3.select("circle")
.each(drag.on("end"));
})
circle {
cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<button>Trigger Drag End On Blue Circle</button>
D3v6
In d3v6 the signatures of functions passed to selection.each()
and drag.on()
are different. The datum is the first parameter of the former and the second parameter of the latter. So we could use Function.apply() within selection.each()
to trigger the end function and pass the proper this
and d
while passing null
for the event data.
var svg = d3.select("body")
.append("svg")
.attr("width",500)
.attr("height",300);
var data = [{x:40,y:100},{x:250,y:100}];
var circles = svg.selectAll(null)
.data(data)
.enter()
.append("circle")
.attr("r", 10)
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("fill", function(d,i) {
return ["steelblue","crimson"][i]
})
var drag = d3.drag()
.on("drag", drag)
.on("end", dragend)
circles.call(drag);
d3.select("button").on("click", function() {
var circle = d3.select("circle")
.each(function(d) {
dragend.apply(this,[null,d])
})
})
function dragend(event,d) {
console.log(d.x+","+d.y);
d3.select(this)
.transition()
.attr("r", 30)
.transition()
.attr("r", 10);
}
function drag(event,d) {
d.x = event.x; d.y = event.y;
d3.select(this)
.attr("cx", d.x)
.attr("cy", d.y);
}
circle {
cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.0.0/d3.min.js"></script>
<button>Trigger Drag End On Blue Circle</button>
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