javascript 如何创建“svg”对象而不附加它?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18455282/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to create "svg" object without appending it?
提问by kjo
Consider the following code:
考虑以下代码:
var svg = d3.select('#somediv').append("svg").attr("width", w).attr("height", h);
I would like to refactor this code so that it reads more like this:
我想重构这段代码,使其更像这样:
var svg = makesvg(w, h);
d3.select("#somediv").append(svg);
Note that, in contrast to the situation shown in the first version, in this second version append
does not createthe "svg" object; it only appends it to d3.select("#somediv")
.
请注意,与第一个版本中显示的情况相反,在第二个版本append
中不会创建“svg”对象;它只将它附加到d3.select("#somediv")
.
The problem is how to implement the function makesvg
. This in turn reduces to the problem: how to instantiate an "svg" object without using append
to do this, since one could then do something like:
问题是如何实现这个功能makesvg
。这反过来又变成了一个问题:如何在不使用的情况下实例化“svg”对象append
,因为然后可以执行以下操作:
function makesvg(width, height) {
return _makesvg().attr("width", w).attr("height", h);
}
So my question boils down to what is the generic equivalent of the hypothetical _makesvg()
factory mentioned above?
所以我的问题归结为_makesvg()
上面提到的假设工厂的通用等价物是什么?
采纳答案by Drew Noakes
You can use the following:
您可以使用以下内容:
var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
Note the use of createElementNS
. This is required because svg
elements are not in the same XHTML namespace as most HTML elements.
注意使用createElementNS
. 这是必需的,因为svg
元素与大多数 HTML 元素不在同一个 XHTML 命名空间中。
This code creates a new svg
element, as you would regardless of using D3 or not, and then creates a selection over that single element.
此代码创建一个新svg
元素,无论是否使用 D3,您都会这样做,然后在该单个元素上创建一个选择。
This can be made marginally more succinct but clearer and less error prone as:
这可以稍微更简洁但更清晰,更不容易出错,因为:
var svg = document.createElementNS(d3.ns.prefix.svg, 'svg');
回答by Paul
To save a little bit of time you can use d3.ns.prefix.svg
为了节省一点时间,您可以使用d3.ns.prefix.svg
var svg = document.createElementNS(d3.ns.prefix.svg, 'svg');
回答by altocumulus
Finally, with the release of D3 v5(March 22nd, 2018) this can now be done in D3 itself. This does not affect the other answers, whatsoever, which are still valid and in the end, D3 is going to use document.createElementNS()
like described in previous posts.
最后,与D3的发布V5(3月22日第二,2018),但是现在可以在D3自身完成。这不会影响其他仍然有效的答案,最终,D3 将document.createElementNS()
像以前的帖子中描述的那样使用。
As of v5 you can now use:
从 v5 开始,您现在可以使用:
Given the specified element name, returns a single-element selection containing a detached element of the given name in the current document.
给定指定的元素名称,返回包含当前文档中给定名称的分离元素的单元素选择。
This new feature can be used as follows:
此新功能可按如下方式使用:
// 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());
Have a look at the following snippet for a working demo creating a detached sub-tree, which is attached on a click event:
查看以下代码片段,了解创建附加在单击事件上的分离子树的工作演示:
// 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>
The main advantages of this approach are ease of use and clarity. The creation of the element is done by D3 behind the scenes and it is made available as a full-fledged selection with all its methods at hand. Apart from manipulating the newly created detached element, the returned selection can easily be used for data-binding.
这种方法的主要优点是易于使用和清晰。元素的创建是由 D3 在幕后完成的,它可以作为一个成熟的选择及其手头的所有方法使用。除了操作新创建的分离元素外,返回的选择还可以轻松用于数据绑定。
It is also worth noting, that the function is not restriced to create elements from the SVG namespace but can be used to create an element from any namespaceregistered with D3.
还值得注意的是,该函数不限于从 SVG 命名空间创建元素,但可用于从任何注册到 D3 的命名空间创建元素。
回答by altocumulus
Here's an example function that creates an unattached group element:
这是一个创建独立组元素的示例函数:
function createSomething(){
return function(){
var group = d3.select(document.createElementNS(d3.ns.prefix.svg, 'g'));
// Add stuff...
return group.node();
}
}
You can call it like so:
你可以这样称呼它:
node.append(createSomething());
Explanation
解释
Let's say you are rendering a collapsible tree and you want to have plus/minus icons with a circle border as the toggles. Your draw function is already enormous so you want the code for drawing the plus sign in it's own function. The draw/update method will take care of proper positioning.
假设您正在渲染一棵可折叠的树,并且您希望使用带有圆形边框的加号/减号图标作为切换开关。您的绘图功能已经很大,因此您需要在其自己的功能中绘制加号的代码。绘制/更新方法将负责正确定位。
One option is to pass the existing container into a function:
一种选择是将现有容器传递给函数:
createPlus(node).attr({
x: 10,
y: 10
});
function createPlus(node){
var group = node.append('g');
// Add stuff...
return group;
}
We can make this better by applying the technique from @Drew and @Paul for creating unattached elements.
我们可以通过应用来自 @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;
}
Except that throws an error because append()
expects either a string or a function.
除了抛出错误,因为append()
需要一个字符串或一个函数。
The name may be specified either as a constant string or as a function that returns the DOM element to append.
该名称可以指定为常量字符串或返回要追加的 DOM 元素的函数。
So we just change it to:
所以我们只需将其更改为:
node.append(function(){
return createPlus();
});
But that still doesn't work. It causes the following error:
但这仍然不起作用。它会导致以下错误:
TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.
Luckily I found selection.node()
which does work! Though, admittedly, I have no idea why.
幸运的是我发现selection.node()
哪个有效!虽然,不可否认,我不知道为什么。
function createPlus(){
var group = d3.select(document.createElementNS(d3.ns.prefix.svg, 'g'));
// Add stuff...
return group.node();
}
We can save us a little more time by moving the anonymous function into createPlus
:
我们可以通过将匿名函数移动到createPlus
:
node.append(createPlus())
function createPlus(){
return function(){
var group = d3.select(document.createElementNS(d3.ns.prefix.svg, 'g'));
// Add stuff...
return group.node();
}
}
回答by brichins
D3 author Mike Bostock suggests another (simpler) approach in his comment on an old D3 Github "issue"asking about this very topic:
D3 作者 Mike Bostock 在他对旧的 D3 Github“问题”的评论中提出了另一种(更简单的)方法,询问了这个主题:
Another strategy you could consider is to remove the element from the DOM immediately after creating it:
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());
您可以考虑的另一种策略是在创建元素后立即从 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());
This approach does indeed append the element on creation, but .remove
s it immediately prior to manipulation and creation of child elements, so there should be no browser repaint. While technicallycontrary to the original question, this is probably the most idiomatic way to meet the requirement.
这种方法确实在创建时附加元素,但.remove
它是在操作和创建子元素之前立即添加的,因此不应该有浏览器重绘。虽然在技术上与原始问题相反,但这可能是满足要求的最惯用的方法。
回答by Sagi
I am working with version 4.4.4
我正在使用 4.4.4 版
var svg = document.createElementNS(d3.namespaces.svg, "svg");