d3.js数据绑定需要来自csv文件

时间:2015-07-31 12:52:04

标签: javascript csv d3.js coffeescript

代码修改为Jim Vallandingham's 'Building a Bubble Cloud',可在github上找到。

csv中的数据:

"miller","husband"
"miller","wife"
"shulz","husband"

with .data(data,function(d){return idValue(d);}); - 其中idValue(d)与第一列有关(由于其他原因必须是这样)

如果行在第一列中包含重复的条目(此处:' miller,miller'),则第二行将返回为' null'在labelEnter中。为什么?

updateLabels = function(d) {


label = labelG.selectAll(".bubble-label")
              .data(data, function(d) {return idValue(d);});

label.exit().remove();

labelEnter = label.enter()
                  .append("a")
                  .attr("class", "bubble-label")
                  .attr("href", function(d) {return "#" + (encodeURIComponent(idValue(d)));})
                  .call(force.drag)
                  .call(connectEvents);

断点:

label - array with [42] entries 
label.exit().remove(); - array with [0] entries
labelEnter - array with [42] entries, rows 'duplic_in_col1' returned as 'null'
来自Tom和Cool Blue的回复的

编辑:,我了解到每个页面元素都需要一个单独的键才能显示。在这个例子中,米勒'作为用于绑定的键出现两次,并且仅使用一次(参见Tom的第一条评论)。这回答了我最初的问题。

如果我改为使用.data(数据),那么数组索引将用于数据绑定。 '米勒'和米勒'然后每个都有自己的索引,两者都将显示为页面元素。这也很有效,并反映在Coll Blue的答案中。

然而, - 新信息 - ,该设计包含clickBubble函数,其中需要知道气泡的关键点:

 removeBubble = (d) ->
 currentId = idValue(d)
 data = data.filter (e) -> idValue(e) != currentId
 update()
 d3.event.preventDefault()

为了保留这个功能,我决定在我的file.csv中添加一个列,并将这些条目作为一个单独的键使用:

"col1","col2","col3"
"1","miller","husband"
"2",""miller","wife"
"3",""shulz","husband"

使用.data(data,function(d){return d.col1;})对第一列进行数据绑定,并将此更改应用于removeBubble函数(d.col1)以识别单击的气泡在移除。

这不是d3.js解决方案,可以创建一个唯一的密钥。如何做到这一点可以在下面的答案中找到。

1 个答案:

答案 0 :(得分:2)

修复症状

为了让它正常运行,试试这个...

updateLabels = function(d) {

//UPDATE SELECTION
label = labelG.selectAll(".bubble-label")
    .data(data, function(d) {return idValue(d);});

//EXIT SELECTION
label.exit().remove();

//ENTER SELECTION
labelEnter = label.enter();

//MERGED UPDATE+ENTER SELECTIONS
label.append("a")
    .attr("class", "bubble-label")
    .attr("href", function(d) {return "#" + (encodeURIComponent(idValue(d)));})
    .call(force.drag)
    .call(connectEvents);  

我建议非常仔细地研究this series of examples

你不是很清楚你要用关键功能实现什么,所以不要试图弄明白,我使用上面的模式使它无关紧要。 @Tom P是正确的:如果密钥不是唯一的,那么它确实没有意义,他建议将其遗漏也将解决问题。 完整的解决方案是创建一个唯一的密钥,并使用上面链接的示例中使用的模式。

如果您有非唯一键,如果已存在匹配键的节点,则第一个节点将被置于更新选择中,其余节点将被置于输入选择中。如果您只在输入选择上添加功能(如在OP代码中),则不会包含第一个节点。因此,如果有两个节点具有相同的密钥,则第一个节点将被省略。在上面的解决方案中,在调用输入选择之后,在更新选择上添加了功能。如文档和示例中所述,enter方法具有将自身合并到更新选择中的副作用,因此无论密钥如何,都会对所有节点进行更改。

修复架构

将它提升到一个新的水平,你可以创建一个键函数,为每个记录返回一个唯一的键,换句话说,假设你的数据是这样的......

[ ["miller","husband"], ["millsbaner","wife"], ["shulz","husband"], ... ]

密钥可能只是d.join("_");

现在,请阅读this
特别是这部分...

  

在数据绑定过程中,键函数被调用两次,   分两个阶段进行。

     
      
  1. 在节点上评估 key 函数,以nodeByKeyValue形成this(节点的关联数组)   context作为节点,d作为节点__data__成员和   第二个参数i作为选择组索引。

  2.   
  3. key 函数在 values 数组的每个元素上进行评估 - 这次使用 values 作为{{1} } context, values [this]   作为第一个参数i索引d作为第二个参数   参数 - 然后结果用于尝试查找   i集合中的节点。如果查找是   成功后,节点将添加到更新选择,任何节点都不会   查询将添加到退出选择中。任何数据元素   未能找到匹配的节点用于形成输入选择。

  4.   

了解一下,如果您更改数据数组中某个元素的内容值,这一点非常重要,例如: nodeByKeyValue,然后绑定到DOM元素的数据也将更改(例如,data[0][0] = "Smith".text(function(d){ return idValue(d) }返回的值也将更新)。为什么?因为绑定到DOM元素的值是.datum()对象的引用,而不是它的内容
所以你必须要小心 如果您通过将新记录推入其中来更改数据,那么这不是问题。但是,最安全的方法是制作一个避免这个问题的密钥。

执行此操作的一种方法是包含将数据转换为键中DOM元素属性的函数。例如......

data[0][0]

所以现在,当function idValue(d) { return d.join("_"); } function encodeURI(d) { return "#" + encodeURIComponent(idValue(d)); } updateLabels = function(d) { //UPDATE SELECTION label = labelG.selectAll(".bubble-label") .data(data, function(d) { return Array.isArray(this) ? encodeURI(d) : d3.select(this).attr("href"); }); //EXIT SELECTION label.exit().remove(); //ENTER SELECTION labelEnter = label.enter() .append("a") .attr("class", "bubble-label"); .attr("href", encodeURI) .call(force.drag) .call(connectEvents); 正在解析节点时,它将从他们的.data属性生成密钥,并且在数据绑定的第二阶段,它解析数据数组时,它将返回从数据构造的href值。因此,即使您更改了某个数据元素的内容,它仍然会在键上显示不匹配,并且该元素将被放置在输入选择中,并且您的原始模式将会正常。