2013-02-16 58 views
2

我使用d3.js創建大量的svg:橢圓元素(〜5000)。在初始渲染後,一些數據項可以通過後端進行更新(我會知道哪些數據),並且我想改變這些橢圓的顏色(例如)。D3.js - 檢索指定數據子集的DOM子集

是否有一種快速方法來恢復與數據項或項目相關的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 - > data通過domElement的映射。 __data__。有沒有一種快速簡單的方法來計算反向映射,而不是手動緩存這些值?

我需要從數據獲取 - > DOM。

+0

這不是一種通用的方法來解決它,但在你的情況下,如果你已經知道哪些元素會改變,哪些不會,你可以把它們放在'g.will-change'和'g.will-not- change'。然後選擇它們會很容易而且便宜。但是,如果每次改變的子集都不同,可以從幾種不同的方法中選擇一種,我可以在答案中解釋。 – 2013-02-16 07:06:10

+0

不同的數據片斷正在不斷變化。標記DOM不會解決我的問題。我需要實際上能夠僅通過對數據項的引用來更新DOM。 – 2013-02-17 01:57:54

回答

3

只要您保持d3選擇參考活動(在您的示例中爲create),D3正在使用映射將數據鍵映射到更新中的DOM節點,因此它實際上是O(log n)。

我們可以做一些測試與D3更新/數據操作方法VS在子集的循環方法:

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。

+1

小提琴是一個好開始,但不完全。我更新了小提琴,以更準確地反映我正在考慮的權衡。關於評估d3如何在內部執行,我看到您的觀點,但正如您所看到的,DOM節點的直接緩存供選擇的速度比連接子集方法快大約20倍。 http://jsfiddle.net/dapPG/9/ – 2013-02-17 02:17:28

+0

很酷的使用每個。如果你總是可以手動緩存這些值,而perf對你來說是最重要的,那麼這是最快的,因爲沒有更多的從Data - > DOM查找(因爲你有直接的DOM元素列表)。我想只是誤解了你的問題,因爲我以爲你想要另一種方式來使用D3-ish方式/模式從Data> DOM進行映射。 – 2013-02-17 04:16:16

+0

好吧,是的,我正在以另一種方式釣魚。我知道如何解決我的問題,但我會發現自己寫了類似的代碼,並按照我的方式在很多地方做記錄。我的問題是d3是否有一種標準的方式來實現我所尋找的,而不是我手動記帳 - 儘管在進一步的思考中,我猜測答案是「不」,這是因爲我不想「做」它。 d3不知道我會使用這些數據的位置,所以它可能需要跟蹤多個dom節點並區分哪一個我希望在每種情況下,哪些是我不能做的,但我可以(儘管很痛苦)。 – 2013-02-17 05:29:45