Below is an excerpt of my JS code to implement a force layout graph in D3. I have two requirements:
Clicking a node should cause child nodes to be retrieved from the backend and added to the graph (this works).
When dragging a node, it should remain fixed when dragging stops. When that same node is clicked in combination with the CMD or CTRL key, the node should be released again. (this works as well)
My issue is that when I click a node to expand it further, the node becomes fixed as well. Thus, clicking inadvertently caused the drag event to be called. Dragging however does not cause the node to expand further (due to if (d3.event.defaultPrevented) return;
).
function visNetwork(graph_id) {
this.graph_id = graph_id;
/* Some code removed for brevity */
var dragstart = function(d) {
console.log('Detected drag...');
d3.event.sourceEvent.preventDefault();
d3.select(this).classed("fixed", d.fixed = true);
};
// set up the D3 visualisation in the specified element
var w = 2000,
h = 2000;
var vis = d3.select("#svg-container")
.append("svg")
.attr("id", this.graph_id)
//necessary to convert the SVG to canvas
.attr("version", 1.1)
.attr("xmlns", "http://www.w3.org/2000/svg")
//better to keep the viewBox dimensions with variables
.attr("viewBox", "0 0 " + w + " " + h )
.attr("preserveAspectRatio", "xMidYMid meet");
var force = d3.layout.force()
.charge(-1500)
.linkDistance(150)
.size([w, h])
.gravity(.1);
var drag = force.drag()
.on("dragstart", dragstart);
var nodes = force.nodes(),
links = force.links();
var update = function() {
var link = vis.selectAll("line")
.data(links, function(d) {
return d.source.id + "-" + d.target.id;
});
link.enter().insert("line", "g")
.attr("id", function(d) {
return d.source.id + "-" + d.target.id;
})
.attr("class", "link")
.attr("stroke", "#ccc");
link.exit().remove();
var node = vis.selectAll("g.node")
.data(nodes, function(d) {
return d.id;
});
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.on('click', function(d, i) {
if (d3.event.defaultPrevented) {
console.log('Ignoring click event...');
return;
}
console.log('Captured click event...');
//Release fixed node when CTRL or CMD key is pressed
if (d3.event.ctrlKey || d3.event.metaKey) {
d3.select(this).classed("fixed", d.fixed = false);
return;
}
modifyGraph(d.id, d3.event);
})
.call(drag);
nodeEnter.append("svg:circle"); /* Code removed for brevity */
nodeEnter.append("svg:text"); /* Code removed for brevity */
node.exit().remove();
force.on("tick", function() { /* Code removed for brevity */ });
// Restart the force layout.
force.start();
};
update();
}