如何访问与 D3 SVG 对象相关的 DOM 元素?

Posted

技术标签:

【中文标题】如何访问与 D3 SVG 对象相关的 DOM 元素?【英文标题】:How to access the DOM element that correlates to a D3 SVG object? 【发布时间】:2012-05-07 10:26:12 【问题描述】:

我正在尝试通过尝试他们的basic bubblecharts 之一来学习 D3。第一个任务:弄清楚如何拖动一个气泡并让它在被拖动时成为最上面的对象。 (问题是让 D3 的对象模型映射到 DOM,但我会到达那里......)

要拖动它,我们可以简单地使用它们提供的代码调用 d3 的拖动行为:

var drag = d3.behavior.drag()
    .on("dragstart", dragstart)
    .on("drag", dragmove)
    .on("dragend", dragend);

效果很好。拖得好。现在,我们如何让它成为最顶层的项目?在此处搜索“svg z-index”,很快就会发现更改索引的唯一方法是将对象在 DOM 中进一步向下移动。好的。它们并不容易,因为单个气泡没有 ID,但是在控制台中乱来,我们可以找到其中一个气泡对象:

$("text:contains('TimeScale')").parent()

我们可以将它移动到包含 svg 元素的末尾:

.appendTo('svg')

完成后拖动它,它是最顶部的项目。到目前为止,一切都很好,如果您完全在 DOM 中工作。

但是:我真正想做的是在拖动给定对象/气泡时自动发生这种情况。 D3 为dragstart()dragend() 函数提供了一个模型,允许我们在拖动过程中嵌入一个语句来做我们想做的事情。 D3 提供d3.select(this) 语法,允许我们访问您当前拖动的对象/气泡的d3 对象表示。但是我如何干净地将它们返回的大量数组转换为对我可以与之交互的 DOM 元素的引用,并且 - 例如 - 将其移动到 svg 容器的末尾,或者在 DOM 中执行其他引用,例如表单提交?

【问题讨论】:

【参考方案1】:

您还可以通过selection.node() method 获取选择所代表的 DOM 元素

var selection = d3.select(domElement);

// later via the selection you can retrieve the element with .node()
var elt = selection.node();

【讨论】:

这是一个更好的答案。 顺便说一句。这也是d3.js的作者Mike Bostok在他的文章How Selections Work中推荐的方式 请注意,无论选择中的元素数量如何,node() 将始终只返回一个 DOM 元素,这将是选择中第一个元素的节点。【参考方案2】:

SVG 文档中的任何 DOM 元素都有一个 ownerSVGElement 属性,该属性引用它所在的 SVG 文档。

D3 的选择只是嵌套数组,上面有额外的方法;如果你有 .select()ed 单个 DOM 元素,你可以用 [0][0] 得到它,例如:

var foo = d3.select(someDOM);

// later, where you don't have someDOM but you do have foo
var someDom = foo[0][0];
var svgRoot = someDom.ownerSVGElement;

但是请注意,如果您使用的是d3.select(this),那么this 已经 DOM 元素;您无需将其包装在 D3 选择中即可展开。

【讨论】:

哦,哇。那是票。我一直在尝试使用d3.select(this),而不仅仅是this。因此,我可以将document.getElementsByTagName("svg")[0].appendChild(document.getElementById(d3.select(this).attr('id'))); 大大简化为$("svg").append($(this));。谢谢! 或者只是this.ownerSVGElement.appendChild(this); 完美!谢谢:我已经在尝试放弃 Jquery 的参与,但对 D3 语法感到困惑。在任何地方都有简化的 API 吗?我正在阅读文档,但它似乎很分散,到目前为止...... 我对 D3 社区和资源并不深入,所以我不确定。我知道 a) 在许多领域有很多分散和半聚合的例子。 b) official API documentation 是彻底的,但是关于演示的一些事情让我失望,所以 c) 我正在创建 my own view on the documentation(目前不完整)。 这似乎不起作用,至少在 d3 4.0 中不再起作用。即使我的选择中有一个元素,mySel[0] 对我来说也是 undefinedmySel._groups[0][0] 返回 DOM 元素,但这看起来不应该被访问。来自the other answer 的mySel.node() 解决方案看起来像是一种更清洁的方法。【参考方案3】:

您可以在追加时为各个元素分配 ID 和类:

node.append("circle.bubble")
.attr("r", function(d)  return d.r; )
.style("fill", function(d)  return fill(d.packageName); )
.attr("id", function(d, i)  return ("idlabel_" + i))
.attr("class", "bubble")
;

然后您可以使用 selectAll("circle.bubble") 按类选择或按 id 选择并修改属性,如下所示:

var testCircle = svg.select("#idlabel_3")
.style("stroke-width", 8);

【讨论】:

...起初我认为这不是一个答案,但事实证明这是一个带有以下附录的答案:如果您继续在创建时为每个节点分配 ID,那么您可以执行:d3.select(this).attr('id'),你现在有了对 DOM 对象的工作引用。谢谢! 还请注意,您真正要访问的是节点本身的 ID,而不是圈子的节点。因此,将行.attr("id", function(d, i) return ("idlabel_" + i)) 插入以d3.json("data/flare.json", function(json) 开头的函数,而不是插入node.append("circle.bubble") 这是一个很好的观点,尽管有时你确实想访问圆圈本身,如果你正在做的是将样式更改应用于该元素(这是示例来自的代码中发生的事情) .

以上是关于如何访问与 D3 SVG 对象相关的 DOM 元素?的主要内容,如果未能解决你的问题,请参考以下文章

d3创建多个svg元素

如何使用 D3.js 将图像添加到 svg 容器

Renderer2 在使用 d3 js - Angular 4 时不渲染 SVG 元素

d3:数据到元素之间的映射

D3.js使用过程中的常见问题(D3版本D3V4)

d3.js - 当鼠标悬停在 SVG 容器上的这些元素上时,如何将光标设置为手?