我正在尝试创建一个多样化的图例,其中包含圆形三角形,矩形,线条等,我想独立创建它们,然后使用d3来排列它们的位置和颜色,但我如何直接访问这些数据呢? / p>
d3.selectAll('g.legend')
.data([
// is there a way to have d3 create an element in memory but not append it?
{ svgFn: function() { this.append('rect') }, ...otherinfo },
{ svgFn: function() { this.append('circle') }, ...otherinfo },
]).enter()
.append('g')
.append(function(d) { d.svgFn.call(this)})
.attr...
答案 0 :(得分:7)
这个问题是如何基于数据创建元素的变体?或如何动态追加元素?模式。在我看来,你的方法将过于复杂和杂乱,因为你需要复制函数来创建数据中的元素。这似乎不是一个优雅的解决方案。
我更希望仅指定要创建的元素的类型,即数据对象中的{type: "circle"}
,{type: "rect"}
等,并让selection.append()
方法正常工作。此方法将接受回调,而回调可能会评估数据中指定的类型并相应地创建元素:
#选择。追加(类型)<>
[...]
否则,类型可以是为每个所选元素计算的函数,按顺序传递当前数据( d ),当前索引( i ),以及当前组( nodes ),将其作为当前DOM元素。此函数应返回要追加的元素。
这会简化您的代码:
d3.selectAll('g.legend')
.data([
{ type: 'rect', other: info },
{ type: 'circle', other: info }
])
.enter().append('g')
.append(function(d) {
return document.createElementNS(d3.namespaces.svg, d.type);
});
根据user2167582&#39; comment的要求,也可以轻松合并分配属性的解决方案。
对于使用d3-selection-multi模块的D3 v4,您可以使用多值语法传递包含要设置的键值对属性的对象。假设您要创建的元素数组如下所示:
var elementsAndAttributes = [
{ type: 'rect', attrs: { "fill": "blue", "width": "10", "height": "10" } },
{ type: 'circle', attrs: { "fill": "red", "cx": "20", "cy": "20", "r": "10" } }
];
然后,您可以在一次运行中绑定此数据并创建具有其属性的元素:
d3.selectAll('g.legend')
.data(elementsAndAttributes)
.enter().append('g')
.append(function(d) { // Create elements from data
return document.createElementNS(d3.namespaces.svg, d.type); // v4 namespace
})
.attrs(function(d) { // Set the attributes object per element
return d.attrs;
});
当仍然使用D3 v3时,事情有点不同。尽管v3支持内置多值对象配置,但您不允许将对象作为函数的返回值提供(请参阅问题#277 &#34;多值映射支持。&#34; 讨论为什么会这样做。但是,您可以使用selection.each()
来实现相同的目标。
d3.selectAll('g.legend')
.data(elementsAndAttributes)
.enter().append('g')
.append(function(d) { // Create elements from data
return document.createElementNS(d3.ns.prefix.svg, d.type); // v3 namespace
})
.each(function(d) { // Iterate over all appended elements
d3.select(this).attr(d.attrs); // Set the attributes object per element
});
忽略D3引用命名空间常量的方式的差异,使用selection.each()
的最后一个版本实际上将在D3 v3和v4中都有效。
进一步阅读:
答案 1 :(得分:0)
基于altocumulus的回答并阅读doc
selection.append("div");
等效于:
selection.append(d3.creator("div"));
我找到了一种让d3进行工作并且不被名称空间困扰的方法
var svg = d3.select("#chart"); // or any other selection to get svg element
d3.selectAll('g.legend')
.data(elementsAndAttributes)
.enter().append('g')
.append(function(d) { // Create elements from data
return d3.creator(d.type).bind(svg.node())();
})
.attrs(function(d) { // Set the attributes object per element
return d.attrs;
});
请注意直接调用绑定的creator()
,因为回调的结果用作appendChild(child)
的参数。如果bind
未知,则可以使用
.append(function(d) { // Create elements from data
return d3.creator(d.type).call(svg.node());
})
文档中提到this
应该是要附加的父节点,但事实并非如此,this
仅用于获取this.ownerDocument
(=== document (适用于浏览器)),并将creator()
调用的结果附加到正确的(g
)节点上。因此,任何(svg)元素都是正确的。