如何在不触及其他元素的情况下更新d3中的单个数据点?

时间:2014-08-09 20:37:51

标签: javascript svg d3.js

我想更新单个数据点并仅修改它绑定的元素,但我无法弄清楚如何执行此操作。

This article似乎表明var sel = svg.selectAll(...).data(...)提供了一系列更新数据,然后sel.enter(...)代表新数据,之后sel同时代表更新的+新数据。

this jsfiddle example中,我将新元素作为绿色和更新的元素着色为蓝色,但似乎每个现有元素都被着色为蓝色,而不仅仅是自从最后更新。如何更新单个数据?

// ...

function update() {
    // clear old classes
    svg.selectAll("text").attr("class","");

    // join to the new data
    var sel = svg.selectAll("text").data(things);

    // update -- but this actually affects all elements in the selection?
    sel.attr("class","update");

    // enter
    sel.enter()
        .append("text")
        .attr("class","enter")
        .attr("x", function(d,i) { return 20*i; })
        .attr("y", 20);

    // update + enter
    sel.text(function(d) { return d; });

    // exit
    sel.exit().remove();
}

2 个答案:

答案 0 :(得分:1)

正如您所发现的,“更新”选项包括所有现有元素,无论数据是否实际更改,都可以随时更新。

如果要测试新数据是否与旧数据相同或不同,则需要一种方法来保留旧数据以与新数据进行比较。然后,您可以使用选择过滤器来丢弃数据相同的元素。

我已经讨论过这个问题previously on the d3 mailing list。这是我提出的方法:

selection = selection.property(" __oldData__", function(d){ return d; } ); 
  //store the old data as a property of the node
                    .data(newData);  
  //over-write the default data property with new data

selection.enter() /*etc*/;  //handle new elements

selection.filter( function(d) {    
  //test whether the relevant properties of d match the equivalent in the oldData
  //also test whether the old data exists, to catch the entering elements!
                 return ( (this.__oldData__ ) && 
                          (d.value != this.__oldData__.value) );
            })
            .style("fill", "blue");

selection.property("__oldData__", null);     
  //delete the old data once it's no longer needed

你当然可以使用旧数据属性的任何名称,只是在它周围抛出很多“_”字符以避免弄乱任何浏览器的本机DOM属性。之后您不需要删除oldData(下次更新时它将被覆盖),但如果您不经常更新,它可以节省内存以显式释放它。

请注意selection.filter()不保留索引值。如果你需要跟踪i,你可以添加一个额外的步骤来保存索引,然后作为单独的属性(元素或数据对象)进行过滤,或者你可以跳过过滤器而只是做测试直接在style / attr调用中的函数中。

编辑:相对于链接的讨论,我更改了过滤器功能,因此包含更新的元素,而不是新的更新的元素。无论哪种方式,过滤器都会选择过滤函数返回true的元素。

答案 1 :(得分:0)

在你的情况下,搞清楚这一点相对容易。您要将元素的文本设置为数据,因此您需要做的就是在决定数据是否已更新时进行比较:

sel.classed("update", function(d) { return d != d3.select(this).text(); });

完整演示here。有关一般解决方案,请参阅AmeliaBR的回答。