如何在不附加“svg”对象的情况下创建它
How to create "svg" object without appending it?
请考虑以下代码:
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()
工厂的通用等价物是什么?
以下内容:
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');
为了节省一点时间,您可以使用 d3.ns.前缀.svg
var svg = document.createElementNS(d3.ns.prefix.svg, 'svg');
最后,随着 D3 v5 的发布(2018 年 3 月 22日(,现在可以在 D3 本身中完成此操作。这不会影响其他仍然有效的答案,最终,D3 将使用document.createElementNS()
,如以前的帖子中所述。
从 v5 开始,您现在可以使用:
# d3.创建(名称(<>
给定指定的元素名称,返回单个元素选择 在当前中包含给定名称的分离元素 公文。
此新功能可按如下方式使用:
// 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 注册的任何命名空间创建元素。
下面是一个创建未附加组元素的示例函数:
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();
}
}
D3作者Mike Bostock在评论D3 Github的一个旧"问题"时提出了另一种(更简单的(方法,询问了这个话题:
您可以考虑的另一种策略是在创建元素后立即从 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
在操作和创建子元素之前附加元素,因此不应重新绘制浏览器。虽然在技术上与原始问题相反,但这可能是满足要求的最惯用方法。
我正在使用 4.4.4 版
var svg = document.createElementNS(d3.namespaces.svg, "svg");
- 在不知道深度或父属性的情况下从对象中删除属性
- 如何在不使用 new 关键字的情况下从函数创建对象
- 如何在不知道关键字的情况下访问javascript对象值
- 在不使用循环的情况下,从一个数据库字符串值向javascript数组添加多个对象
- 如何在不删除类似侦听器的情况下从父对象中删除jQuery事件侦听器
- 是否可以在不重新渲染的情况下显示/隐藏父对象中的元素
- 在禁用ng的情况下搜索JSON对象(AngularJS)
- 如何在不安装npm的情况下一次性扩展对象
- 是否可以在不创建svg对象的情况下创建捕捉元素?[snap.svg]
- 是否可以在不序列化的情况下将对象从SilverLight传递到JavaScript
- 在知道对象值的情况下,确定数组中JS对象的索引
- 在不重构Mongo数据库的情况下,更新嵌入数组中的具有给定ID的对象
- 如何避免在这种情况下修改事件对象
- handontable:在不更改数据数组/对象的情况下隐藏某些列
- 是否可以在不加对象名称前缀的情况下调用对象的函数
- 在不使用object.Create的情况下创建具有null原型的javascript对象
- 我可以在不使用XMLHttpRequest或文档对象的情况下使用Javascript来获取网页吗
- 如何在不选择模式配置参数的情况下,使用mongoose在MongoDB模式实例化中的关联数组/对象中执行foreach
- 如何在不破坏shouldComponentUpdate的情况下传递对象/子对象作为道具
- 在不使用jQuery的情况下连接两个JSON对象