【问题标题】:Reload d3.js graph on node click在节点单击时重新加载 d3.js 图
【发布时间】:2015-02-23 18:42:09
【问题描述】:

我有一个 d3 图表,其中包含一组基于项目的节点。当我点击其中一个节点时,图表会根据点击的节点重新加载数据。

我使用这样的 URL 结构: http://siteurl.com/index.html?item=

当一个节点被点击时,我有一个函数使用新的 URL 再次运行 d3.json( 函数,然后再次执行更新函数。

我最近更改了我的代码,以便节点词出现在节点下方。 现在我在 node.exit().remove(); 的代码行上收到“未定义不是函数”错误 编辑:从@Elijah 的回答中修复了问题,但没有解决我的问题。

所以当我点击一个节点时,链接会被删除,然后重新生成,但之前图表中的节点仍然存在。

JSFiddle

这是我的一些 JS

$wordToSearch = "bitter";

var w = 960,
    h = 960,
    node,
    link,
    root,
    title;

var jsonURL = 'http://desolate-taiga-6759.herokuapp.com/word/' + $wordToSearch;

d3.json(jsonURL, function(json) {
    root = json.words[0]; //set root node
    root.fixed = true;
    root.x = w / 2;
    root.y = h / 2 - 80;
    update();
});

var force = d3.layout.force()
    .on("tick", tick)
    .charge(-700)
    .gravity(0.1)
    .friction(0.9)
    .linkDistance(50)
    .size([w, h]);

var svg = d3.select(".graph").append("svg")
    .attr("width", w)
    .attr("height", h);



//Update the graph
function update() {
    var nodes = flatten(root),
    links = d3.layout.tree().links(nodes);

    // Restart the force layout.
    force
        .nodes(nodes)
        .links(links)
        .start();

    // Update the links…
    link = svg.selectAll("line.link")
        .data(links, function(d) { return d.target.id; });

    // Enter any new links.
    link.enter().insert("svg:line", ".node")
        .attr("class", "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; });

    // Exit any old links.
    link.exit().remove();

    // Update the nodes…
    node = svg.selectAll(".node")
        .data(nodes)
        .enter().append("g")
        .attr("class", "node")
        .call(force.drag);

    node.append("circle")   
        .attr("r", 10)
        .on("click", click)
        .style("fill", "red");

    node.append("text")
        .attr("dy", 10 + 15)
        .attr("text-anchor", "middle")
        .text(function(d) { return d.word });

    svg.selectAll(".node").data(nodes).exit().remove();

}

function tick() {
    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 + ")"; });
}




/***********************
*** CUSTOM FUNCTIONS ***
***********************/

//Request extended JSON objects when clicking a clickable node
function click(d) {
    $wordClicked = d.word;

    var jsonURL = 'http://desolate-taiga-6759.herokuapp.com/word/' + $wordClicked;
    console.log(jsonURL);

    updateGraph(jsonURL);
}

// Returns a list of all nodes under the root.
function flatten(root) {
    var nodes = [], i = 0;

    function recurse(node) {
        if (node.children) node.size = node.children.reduce(function(p, v) { return p + recurse(v); }, 0);
        if (!node.id) node.id = ++i;
        nodes.push(node);
        return node.size;
    }

    root.size = recurse(root);
    return nodes;

}

//Update graph with new extended JSON objects
function updateGraph(newURL) {
    d3.json(newURL, function(json) {
        root = json.words[0]; //set root node
        root.fixed = true;
        root.x = w / 2;
        root.y = h / 2 - 80;

        update();
    });
}

function getUrlParameter(sParam)
{
    var sPageURL = window.location.search.substring(1);
    var sURLVariables = sPageURL.split('&');
    for (var i = 0; i < sURLVariables.length; i++) 
    {
        var sParameterName = sURLVariables[i].split('=');
        if (sParameterName[0] == sParam)  {
            return sParameterName[1];
        }
    }
} 

请问有人知道为什么那不起作用吗?

编辑:根据@Elijah 的回答更新了我的 JS。

【问题讨论】:

    标签: javascript jquery d3.js


    【解决方案1】:

    分别处理进入、退出和更新这3个状态:

    node = svg.selectAll(".node")
        .data(nodes);  // base data selection, this is the update
    
    var nodeE = node
        .enter();  // handle the enter case
    
    var nodeG = nodeE.append("g")
        .attr("class", "node")
        .call(force.drag);  // add group ON ENTER
    
    nodeG.append("circle")  
        .attr("r", 10)
        .on("click", click)
        .style("fill", "red");  // append circle to group ON ENTER
    
    nodeG.append("text")
        .attr("dy", 10 + 15)
        .attr("text-anchor", "middle")
        .text(function(d) { return d.word }); // append text to group ON ENTER
    
    node.exit().remove();  // handle exit
    

    更新小提琴here。

    【讨论】:

      【解决方案2】:

      你的问题在这里:

      node = svg.selectAll(".node") .data(nodes) .enter().append("g") .attr("class", "node") .call(force.drag);

      您将node 定义为svg.selectAll(".node").enter(),这意味着您的变量现在指的是选择输入行为,而不是选择本身。因此,当您尝试使用以下命令更改退出行为时:node.exit().remove();

      ..您尝试访问的不是选择的 .exit() 行为,而是选择的 .enter() 行为。将其替换为:

      svg.selectAll(".node").data(nodes).exit().remove();

      这应该可以解决您的问题。可能还有其他事情发生,但这肯定会导致问题。

      编辑添加:

      您还应该更新您的刻度函数,使其不引用现在分配给#selection.enter() 的node 而不是选择,而是引用选择: svg.selectAll("g.node") .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });

      【讨论】:

      • 你没看错,这肯定是个问题。但不幸的是,它并没有解决我的问题。我创建了一个小提琴来演示我的问题:jsfiddle.net/tn5yb32t
      • 您的 tick 函数也引用了 node 变量,因为它被分配给 enter() 行为,所以不会更新以处理新创建的节点。
      • @OliverEvans,以利亚的观点,这里有一个清理进入/退出/更新选择的小提琴:jsfiddle.net/tn5yb32t/1,它现在可以工作了。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-26
      • 1970-01-01
      相关资源
      最近更新 更多