D3.js - Dragging API
D3.js - Dragging API
Section titled “D3.js - Dragging API”The D3 Dragging API (d3-drag) provides a convenient way to add drag-and-drop interactions to your SVG or HTML elements. It handles mouse and touch events to allow users to move elements around.
Installation / Setup
Section titled “Installation / Setup”If you’re using the full D3 bundle, d3-drag is included. For modular projects, you’d install and import it:
// With npm or yarnnpm install d3-drag// yarn add d3-drag
// In your JavaScriptimport { drag } from 'd3-drag';// d3-selection is also usually neededimport { select } from 'd3-selection';If using D3 via a CDN:
<script src="https://d3js.org/d3.v7.min.js"></script>Dragging API Methods
Section titled “Dragging API Methods”Key methods related to the D3 Drag API:
d3.drag()
Section titled “d3.drag()”This function creates a new drag behavior. This behavior is then applied to a D3 selection.
const dragBehavior = d3.drag();dragBehavior(selection)
Section titled “dragBehavior(selection)”Applies the drag behavior to the specified D3 selection. This is typically done using selection.call(dragBehavior).
// Example: Make all circles with class 'draggable' respond to draggingd3.selectAll("circle.draggable") .call(d3.drag().on("start drag end", handleDragEvent));
function handleDragEvent(event, d) { // 'event' is the D3 drag event object // 'd' is the datum bound to the dragged element if (event.type === "start") { // console.log("Drag started!", event.subject); } else if (event.type === "drag") { // d3.select(this).attr("cx", event.x).attr("cy", event.y); } else if (event.type === "end") { // console.log("Drag ended!"); }}drag.container([container])
Section titled “drag.container([container])”Sets the container accessor, which determines the coordinate system for drag events (event.x, event.y). The container function is invoked for each event, passed the current event and datum, and this context is the dragged DOM element. It should return the DOM element whose coordinate system is used.
Defaults to the parent of the dragged element for SVG, or the owner SVG element. For HTML elements, it’s often the parent. For dragging elements within a Canvas, you’d typically set the container to the canvas element itself.
dragBehavior.container(function() { return this.parentNode; });// Or for canvas:dragBehavior.container(function() { return canvasElement; });drag.filter([filter])
Section titled “drag.filter([filter])”Sets an event filter function. The drag behavior will only start if the filter function returns true. The filter is passed the current native event (e.g., mousedown, touchstart) and the datum d. this is the DOM element.
The default filter is (event) => !event.ctrlKey && !event.button, which ignores drags initiated with a Ctrl key press or a non-primary mouse button (e.g., right-click).
dragBehavior.filter(function(event) { // Only allow dragging with the primary mouse button (button 0) return event.button === 0;});drag.subject([subject])
Section titled “drag.subject([subject])”Sets the subject accessor. The subject represents the thing being dragged. This function is called when a drag gesture starts (on mousedown or touchstart). It’s passed the native event and the datum d, and this is the DOM element. It should return an object representing the subject. This subject object will be available as event.subject in subsequent drag events (start, drag, end).
The default subject is the datum d of the dragged element: (event, d) => d.
If the subject has x and y properties, these are used to calculate event.dx and event.dy (the change in position since the last drag event).
dragBehavior.subject(function(event, d) { // If 'd' is the datum, it might already have x and y, or you can define them here return d.dataForDragging || { x: event.x, y: event.y, originalData: d };});drag.clickDistance([distance])
Section titled “drag.clickDistance([distance])”Sets the maximum distance (in pixels) a pointer can move between mousedown/touchstart and mouseup/touchend to be considered a click (and thus not initiate a drag or trigger a ‘drag’ event). Defaults to 0. This helps distinguish between clicks and drags.
drag.on(typenames, [listener])
Section titled “drag.on(typenames, [listener])”Attaches an event listener for the specified typenames. typenames is a string of one or more event types, separated by spaces (e.g., “start drag end”).
Valid event types are:
start: Fired when a drag gesture starts (after mousedown/touchstart and filter passes).drag: Fired when the pointer moves during an active drag gesture.end: Fired when the drag gesture ends (on mouseup/touchend).
The listener function is called with (event, d), where event is the D3 drag event object (see below) and d is the datum of the dragged element. this context within the listener is the dragged DOM element.
d3.dragDisable(window)
Section titled “d3.dragDisable(window)”A utility function to prevent the default browser behavior for drag-and-drop and text selection on the specified window. Call this if native drag interactions interfere with D3 drag.
d3.dragEnable(window[, noclick])
Section titled “d3.dragEnable(window[, noclick])”Re-enables default drag-and-drop and text selection if previously disabled by d3.dragDisable. If noclick is true, it also suppresses click events that might follow the mouseup.
The D3 Drag Event Object
Section titled “The D3 Drag Event Object”When a drag event listener is called, it receives a D3 event object with the following useful properties:
target: The drag behavior instance that dispatched the event.type: The type of drag event as a string: “start”, “drag”, or “end”.subject: The current drag subject, as defined bydrag.subject().x: The new x-coordinate of the pointer, relative to thecontainer.y: The new y-coordinate of the pointer, relative to thecontainer.dx: The change in x-coordinate since the last drag event (for “drag” events). Calculated based onevent.subject.xif available.dy: The change in y-coordinate since the last drag event (for “drag” events). Calculated based onevent.subject.yif available.identifier: The string “mouse” for mouse events, or the touch identifier for touch events.active: The number of active drag gestures (useful for multi-touch scenarios, though d3-drag primarily handles single pointer drags).sourceEvent: The underlying native browser event (e.g., MouseEvent, TouchEvent).
Example of a simple draggable circle:
<!DOCTYPE html><html><head> <script src="https://d3js.org/d3.v7.min.js"></script> <style> .draggable { cursor: grab; } .dragging { cursor: grabbing; fill: steelblue; } </style></head><body> <svg width="400" height="300" style="border:1px solid #ccc;"> <circle class="draggable" cx="50" cy="50" r="20" fill="orange"></circle> </svg>
<script> function dragstarted(event, d) { d3.select(this).raise().classed("dragging", true); }
function dragged(event, d) { // Update the circle's subject (its data object `d` if it has x,y) // Or directly update the element's attributes // If `d` is the subject and has x,y, D3 updates them internally for dx/dy calculation // event.subject.x = event.x; // event.subject.y = event.y; d3.select(this).attr("cx", event.x).attr("cy", event.y); }
function dragended(event, d) { d3.select(this).classed("dragging", false); }
const dragBehavior = d3.drag() .on("start", dragstarted) .on("drag", dragged) .on("end", dragended);
d3.selectAll("circle.draggable").call(dragBehavior); </script></body></html>In this example, the circle’s cx and cy attributes are directly updated using event.x and event.y. For more complex scenarios, you might update the event.subject’s x and y properties if your rendering logic depends on the data object rather than direct attribute manipulation in the drag handler.