如何在不附加“svg”对象的情况下创建它

How to create "svg" object without appending it?

本文关键字:对象 情况下 创建 svg      更新时间:2023-09-26

请考虑以下代码:

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");
相关文章: