d3 未 "De-Selecting" 节点正确

d3 Not "De-Selecting" Nodes Properly

我的 d3 力图中有这个 "select node" 功能。由于某种原因,它没有按应有的方式运行。代码应该做到这一点,当用户双击它获得 selected/deselected 的节点时(恢复正常 color/turns 黄色)。当您 selecting 然后 deselecting 节点时一切正常。问题出现在以下情况:

  1. 如果你select一个节点A然后select节点B(此时它们都是黄色的,因为它应该)然后返回并双击节点A去-select 它被读取为再次 selected(保持黄色)。
  2. 当您 select 节点 A 然后 select 节点 B。然后双击节点 B 取消 select 它将按预期工作。但是,当您返回双击节点 A(仍为 selected)以取消 select 时,它将作为 selected.
  3. 读入

以下是我认为问题所在的一些代码片段。

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();
    }
}

所以在我的代码中 node.on('dblclick', highlight) 只是改变当前 selected 节点 属性 是否 selected 。 node.style 正在对 属性 进行实际检查并更改颜色。

任何关于为什么会发生这种奇怪行为的帮助都将不胜感激!

如果你 select A,那么 selected_node 会设置为 A。

然后,如果你 select B,那么 selected_node 会设置为 B

然后,如果您再次单击 A,则在 highlight() 内,表达式 mousedown_node != selected_node 的计算结果为 true,因为 A 即 mousedown_node 不等于 selected_node,还是B,从前面的selection.

所以这是一个错误。

如果您允许多 selection,那么单个变量 selected_node 无法捕获 selection 状态。如果您有一个 selected_nodes Array,您可以从中添加和删除节点,那么您可以检查是否 selected_nodes.indexOf(mousedown_node) > -1 以确定它是否被 selected。

但实际上,我完全不明白您为什么需要所有这些逻辑 — 除非您没有包含的某些代码依赖于 selected_node。真的,您所有的突出显示功能都需要

function highlight(d) {
  d.selected = !d.selected;
  update();
}

这应该可以解决您的问题。