代码修改为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解决方案,可以创建一个唯一的密钥。如何做到这一点可以在下面的答案中找到。
答案 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,
特别是这部分...
在数据绑定过程中,键函数被调用两次, 分两个阶段进行。
在节点上评估 key 函数,以
nodeByKeyValue
形成this
(节点的关联数组) context作为节点,d
作为节点__data__
成员和 第二个参数i
作为选择组索引。- 醇>
key 函数在 values 数组的每个元素上进行评估 - 这次使用 values 作为{{1} } context, values [
this
] 作为第一个参数i
和值索引d
作为第二个参数 参数 - 然后结果用于尝试查找i
集合中的节点。如果查找是 成功后,节点将添加到更新选择,任何节点都不会 查询将添加到退出选择中。任何数据元素 未能找到匹配的节点用于形成输入选择。
了解一下,如果您更改数据数组中某个元素的内容值,这一点非常重要,例如: 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值。因此,即使您更改了某个数据元素的内容,它仍然会在键上显示不匹配,并且该元素将被放置在输入选择中,并且您的原始模式将会正常。