I have this "select node" functionality in my d3 force-graph. For some reason it is not behaving as it should. The code should make it so when a user double clicks the node it gets selected/deselected (Goes back to normal color/turns yellow). Everything works when you are selecting then deselecting a node. The issue comes in the following cases:
Here are some code snippets from where I think the issue is.
var edges = [];
var nodes = [];
var nodesHash = {};
function update() {
// clear stack of selected nodes
selectedNodes = [];
// Update link data based on edges array.
link = link.data(edges);
// Create new links
link.enter().append("line")
.attr("class", "link")
.style("stroke-width", 1.5);
// Delete removed links
link.exit().remove();
// Update node data based on nodes array.
node = node.data(nodes);
// Create new nodes
node.enter().append("g")
.attr("class", "node")
.attr("id", function(d) { return d.data['id'] })
.call(force.drag)
.on('mouseover', connectedNodes)
.on('mouseleave', restore)
.on('dblclick', highlight);
// Delete removed nodes
node.exit().remove();
node.append("circle").attr("r", 11);
node.classed("selected", function(d) { return d === d.selected; })
// Node behavior for checking if selected otherwise colors nodes to color given from JSON.
node.style("fill", function(d) {
if (d.selected === false) {
return d.data['color']
update();
}
else {
return "yellow";
update();
}
}).select("circle").style("stroke", "black");
// Link color based on JSON data.
link.style("stroke", function(d) { return d.data['color'] });
// Adds text to nodes
node.append("text")
.attr("dx", 12)
.attr("dy", ".35em")
.style("fill", "black")
.text(function (d) { return d.data['label']; });
// Creates an index used to figure out neighbor nodes.
root.edges.forEach(function (d) {
linkedByIndex[d.data.source + "," + d.data.target] = 1;
});
// responsive behavior for graph based on window.
window.addEventListener('resize', resize);
force.on("tick", function() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
});
force.start();
}
// Reset Mouse Variables
function resetMouseVars() {
mousedown_node = null;
mouseup_node = null;
mousedown_link = null;
}
// Highlighting of selected node.
function highlight(d) {
mousedown_node = d;
if (mousedown_node != selected_node) {
console.log("Selected: " + mousedown_node.data['id']);
selected_node = mousedown_node;
selected_node.selected = true;
}
else {
console.log("De-Selected: " + mousedown_node.data['id']);
selected_node = mousedown_node;
selected_node.selected = false;
}
resetMouseVars();
update();
}
function spliceLinksForNode(node) {
toSplice = edges.filter(
function(e) {
return (e.source === node) || (e.target === node); });
toSplice.map(
function(e) {
edges.splice(edges.indexOf(e), 1); });
}
// Delete node with prompt
function deleteNode() {
console.log("Prompted to delete: " + selected_node);
if (confirm("Deleting selected elements will remove them from the graph entirely. Are you sure?")) {
if (!selected_node) alert("No node selected");
if (selected_node) {
console.log("Deleted: " + selected_node);
selected_node.removed = true;
nodes.splice(nodes.indexOf(selected_node), 1);
spliceLinksForNode(selected_node);
}
selected_node = null;
update();
}
}
So in my code the node.on('dblclick', highlight)
is just changing the currently selected nodes property of whether it is selected or not. The node.style
is doing the actual checking of the property and changing the color.
Any assistance on why this odd behavior is happening will be appreciated!
If you select A, then selected_node
gets set to A.
Then, if you select B, then selected_node
gets set to B
Then, if you click A again then, inside highlight()
, the expression mousedown_node != selected_node
evaluates to true
because A, which is mousedown_node
is not equal selected_node
, which is still B, from the previous selection.
So that's a bug.
If you are allowing multi-selection then there's no way a single variable selected_node
is sufficient to capture the selection state. If you had a selected_nodes
Array from which you add and remove nodes, then you can check if selected_nodes.indexOf(mousedown_node) > -1
to determine if it's selected.
But really, I don't see why you need all this logic at all — unless maybe some code that you didn't include is relying on selected_node
. Really, all your highlight function needs to be is just
function highlight(d) {
d.selected = !d.selected;
update();
}
And that should fix your problem.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.