请考虑以下代码:
var svg = d3.select('#somediv').append("svg").attr("width", w).attr("height", h);
我想重构这段代码,以便它更像这样:
var svg = makesvg(w, h);
d3.select("#somediv").append(svg);
请注意,与第一个版本中显示的情况相反,在第二个版本中append
不会创建“svg”对象;它只会将其附加到d3.select("#somediv")
。
问题是如何实现函数makesvg
。这反过来又减少了问题:如何在不使用append
的情况下实例化“svg”对象,因为可以执行以下操作:
function makesvg(width, height) {
return _makesvg().attr("width", w).attr("height", h);
}
所以我的问题归结为上面提到的假设_makesvg()
工厂的通用等价物?
答案 0 :(得分:29)
您可以使用以下内容:
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
请注意createElementNS
的使用。这是必需的,因为svg
元素与大多数HTML元素不在同一个XHTML名称空间中。
此代码创建一个新的svg
元素,无论是否使用D3,都会创建一个新的var svg = document.createElementNS(d3.ns.prefix.svg, 'svg');
元素,然后在该单个元素上创建一个选择。
这可以略微简洁但更清晰,更不容易出错:
{{1}}
答案 1 :(得分:24)
为了节省一点时间,您可以使用 d3.ns.prefix.svg
var svg = document.createElementNS(d3.ns.prefix.svg, 'svg');
答案 2 :(得分:6)
这是一个创建未附加组元素的示例函数:
function createSomething(){
return function(){
var group = d3.select(document.createElementNS(d3.ns.prefix.svg, 'g'));
// Add stuff...
return group.node();
}
}
您可以这样称呼它:
node.append(createSomething());
让我们假设您正在渲染一个可折叠的树,并且您希望使用带有圆边框的加/减图标作为切换。你的绘图功能已经非常庞大,所以你想要代码在它自己的函数中绘制加号。绘制/更新方法将负责正确定位。
一种选择是将现有容器传递给函数:
createPlus(node).attr({
x: 10,
y: 10
});
function createPlus(node){
var group = node.append('g');
// Add stuff...
return group;
}
我们可以通过应用@Drew和@Paul中的技术来创建未附加的元素,从而使这更好。
node.append(createPlus())
.attr({
x: 10,
y: 10
});
function createPlus(){
var group = d3.select(document.createElementNS(d3.ns.prefix.svg, 'g'));
// Add stuff...
return group;
}
除了抛出错误,因为append()
需要字符串或函数。
可以将名称指定为常量字符串或返回要追加的DOM元素的函数。
所以我们只需将其更改为:
node.append(function(){
return createPlus();
});
但这仍然无法奏效。它会导致以下错误:
TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.
幸运的是我发现selection.node()
确实有效!虽然,诚然,我不知道为什么。
function createPlus(){
var group = d3.select(document.createElementNS(d3.ns.prefix.svg, 'g'));
// Add stuff...
return group.node();
}
通过将匿名函数移动到createPlus
:
node.append(createPlus())
function createPlus(){
return function(){
var group = d3.select(document.createElementNS(d3.ns.prefix.svg, 'g'));
// Add stuff...
return group.node();
}
}
答案 3 :(得分:6)
最后,随着D3 v5 (3月22日 nd ,2018年)的发布,现在可以在D3本身中完成。这不会影响其他答案,无论如何,这些答案仍然有效,最后,D3将使用document.createElementNS()
,就像之前的帖子中所描述的那样。
从v5开始,您现在可以使用:
给定指定的元素 name ,返回单元素选择 在当前包含给定名称的分离元素 文档。
这个新功能可以按如下方式使用:
// Create detached <svg> element.
const detachedSVG = d3.create("svg");
// Manipulate detached element.
detachedSVG
.attr("width", 400)
.attr("height", 200);
// Bind data. Append sub-elements (also not attached to DOM).
detachedSVG.selectAll(null)
.data([50, 100])
.enter()
//...
// Attach element to DOM.
d3.select("body")
.append(() => detachedSVG.node());
请查看以下代码片段,了解创建分离子树的工作演示,该子树附加在点击事件中:
// Create detached <svg> element.
const detachedSVG = d3.create("svg");
// Manipulate detached element.
detachedSVG
.attr("width", 400)
.attr("height", 200);
// Bind data. Attach sub-elements.
detachedSVG.selectAll(null)
.data([50, 100])
.enter().append("circle")
.attr("r", 20)
.attr("cx", d => d)
.attr("cy", 50);
// Still detached. Attach on click.
d3.select(document)
.on("click", () => {
// Attach element to DOM.
d3.select("body")
.append(() => detachedSVG.node());
});
<script src="https://d3js.org/d3.v5.js"></script>
这种方法的主要优点是易于使用和清晰。元素的创建由D3在幕后完成,并且可以作为一个完整的选择,包含所有方法。除了操作新创建的分离元素之外,返回的选择可以很容易地用于数据绑定。
值得注意的是,该函数不限制从SVG名称空间创建元素,但可用于从D3注册的任何namespace创建元素。
答案 4 :(得分:2)
您可以考虑的另一个策略是在创建DOM后立即从DOM中删除该元素:
var svg = d3.select("body").append("svg") .remove() .attr("width", w) .attr("height", w); svg.append("circle") .attr("r", 200); document.body.appendChild(svg.node());
这种方法确实在创建时附加了元素,但是.remove
它在操作和创建子元素之前就已经存在,因此不应该重复浏览器。虽然技术上与原始问题相反,但这可能是满足要求的最惯用的方式。
答案 5 :(得分:1)
我正在使用4.4.4版
var svg = document.createElementNS(d3.namespaces.svg, "svg");