D3.js - 检索给定数据子集的DOM子集

时间:2013-02-16 00:47:00

标签: javascript d3.js

我正在使用d3.js创建大量的svg:ellipse元素(~5000)。在初始渲染之后,可以通过后端更新一些数据项(我将知道哪些数据项),并且我想更改这些省略号的颜色(例如)。

是否有快速方法来恢复与数据项或项相关联的DOM元素?除了明显的技术,如果使用数据子集重新计算整个DOM元素集合的连接?

var myData = [{ id: 'item1'}, { id: 'item2' }, ... { id: 'item5000' }];
var create = d3.selectAll('ellipse).data(myData, function(d) { return d.id; });
create.enter().append('ellipse').each(function(d) {
    // initialize ellipse
});

// later on

// this works, but it seems like it would have to iterate over all 5000 elements
var subset = myData.slice(1200, 1210); // just an example
var updateElements = d3.selectAll('ellipse').data(subset, function(d) { return d.id; });
updateElements.each(function(d) {
    // this was O(5000) to do the join, I _think_
    // change color or otherwise update
});

我每秒都会多次渲染更新(尽可能快,真的如此),似乎O(5000)更新了一些元素很多。

我在考虑这样的事情:

create.enter().append('ellipse').each(function(d) {
    d.__dom = this;
    // continue with initialization
});

// later on

// pull the dom nodes back out
var subset = myData.slice(1200, 1210).map(function(d) { return d.__dom; });
d3.selectAll(subset).each(function(d) {
    // now it should be O(subset.length)
});

这很有效。但似乎这是一个常见的模式,所以我想知道是否有一种标准方法来解决这个问题?我实际上想在多个渲染中使用我的数据,所以我需要更加聪明,这样他们就不会相互绊倒。

基本上,我知道d3提供了来自DOM的地图 - >数据来自domElement。__data__。有没有一种快速简便的方法来计算反向映射,除了手动缓存值?

我需要从数据中获取 - > DOM。

1 个答案:

答案 0 :(得分:3)

只要您保持d3选择引用处于活动状态(示例中为create),D3就会使用映射将数据键映射到更新中的DOM节点,因此它实际上是O(log n)。

我们可以使用D3更新/数据运算符方法对子集上的循环方法进行一些测试:

var d3UpdateMethod = function() {
    svg.selectAll("ellipse").data(subset, keyFunc)
        .attr("style", "fill:green");
}
var loopMethod = function() {
    for (var i=0; i < subset.length; i++) {
        svg.selectAll(".di" + i)
        .attr("style", "fill:green");
    }
}
var timedTest = function(f) {
    var sumTime=0;
    for (var i=0; i < 10; i++) {
        var startTime = Date.now();
        f();
        sumTime += (Date.now() - startTime);
    }
    return sumTime / 10;
};
var nextY = 100;
var log = function(text) {
    svg.append("text")
        .attr("x", width/2)
        .attr("y", nextY+=100)
        .attr("text-anchor", "middle")
        .attr("style", "fill:red")
        .text(text);
};

log("d3UpdateMethod time:" + timedTest(d3UpdateMethod));
log("loopMethod time:" + timedTest(loopMethod));

我还创造了一个小提琴来展示我理解你正在尝试做的事情here


使跟踪子集中的节点变得容易的另一种方法是向子集添加CSS类。例如:

var ellipse = svg.selectAll("ellipse").data(data, keyFunc).enter()
    .append("ellipse")
    .attr("class", function (d) { 
        var cl = "di" + d.i;
        if (d.i % 10 == 0)
            cl+= " subset"; //<< add css class for those nodes to be updated later
        return cl;
    })
    ...

请注意,“子集”类如何仅添加到您知道的子集中的节点,以便稍后更新。然后,您可以稍后选择它们以进行更新,其中包含以下内容:

svg.selectAll("ellipse.subset").attr("style", "fill:yellow");

我更新了fiddle以包含此测试,它几乎与directMethod一样快。