d3没有正确地“取消选择”节点

时间:2015-05-15 19:16:41

标签: javascript d3.js force-layout

我的d3强制图中有这个“选择节点”功能。由于某种原因,它不应该表现得如此。当用户双击它被选中/取消选择的节点时,代码应该这样做(返回正常颜色/变黄)。当您选择然后取消选择节点时,一切正常。问题出在以下情况:

  1. 如果选择节点A然后选择节点B(此时它们都是黄色的),然后返回并双击节点A取消选择它将被读取为再次被选中(保持黄色)。
  2. 选择节点A然后选择节点B.然后双击节点B取消选择它将按预期工作。但当你回到双击节点A(仍然被选中)取消选择时,它将被读入所选。
  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)只是更改当前选择的节点属性,无论它是否被选中。 node.style正在对属性进行实际检查并更改颜色。

    对于这种奇怪行为发生的原因,我们将不胜感激!

1 个答案:

答案 0 :(得分:2)

如果选择A,则selected_node将设置为A.

然后,如果您选择B,则selected_node将设置为B

然后,如果您再次点击A,那么在highlight()内,表达式mousedown_node != selected_node的评估结果为true,因为A mousedown_node不等于selected_node来自之前的选择,仍然是B。

这是一个错误。

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

但实际上,我不明白为什么你需要所有这些逻辑 - 除非你没有包含的一些代码依赖于selected_node。真的,你所有的高亮功能都需要是

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

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