使用D3更新SVG元素Z索引

使用D3库将SVG元素置于z顺序的顶部是一种有效的方法?

我的具体情况是一个饼图,它突出显示(通过添加一个strokepath ),当鼠标在给定的一块。 生成我的图表的代码块如下:

 svg.selectAll("path") .data(d) .enter().append("path") .attr("d", arc) .attr("class", "arc") .attr("fill", function(d) { return color(d.name); }) .attr("stroke", "#fff") .attr("stroke-width", 0) .on("mouseover", function(d) { d3.select(this) .attr("stroke-width", 2) .classed("top", true); //.style("z-index", 1); }) .on("mouseout", function(d) { d3.select(this) .attr("stroke-width", 0) .classed("top", false); //.style("z-index", -1); }); 

我已经尝试了几个选项,但目前还没有运气。 使用style("z-index")和调用classed都没有工作。

在我的CSS中,“top”类定义如下:

 .top { fill: red; z-index: 100; } 

fill声明在那里,以确保我知道它正确地打开/closures。 它是。

我听说使用sort是一种select,但我不清楚如何实现将“选定”元素置顶。

更新:

我用下面的代码解决了我的特殊情况,它在mouseover事件中向SVG添加了一个新的弧,以显示一个亮点。

 svg.selectAll("path") .data(d) .enter().append("path") .attr("d", arc) .attr("class", "arc") .style("fill", function(d) { return color(d.name); }) .style("stroke", "#fff") .style("stroke-width", 0) .on("mouseover", function(d) { svg.append("path") .attr("d", d3.select(this).attr("d")) .attr("id", "arcSelection") .style("fill", "none") .style("stroke", "#fff") .style("stroke-width", 2); }) .on("mouseout", function(d) { d3.select("#arcSelection").remove(); }); 

开发人员提出的解决scheme之一是:“使用D3的sorting操作符重新sorting元素”。 (请参阅https://github.com/mbostock/d3/issues/252

从这个angular度来看,可以通过比较数据来对元素进行分类,或者如果它们是无数据元素,

 .on("mouseover", function(d) { svg.selectAll("path").sort(function (a, b) { // select the parent and sort the path's if (a.id != d.id) return -1; // a is not the hovered element, send "a" to the back else return 1; // a is the hovered element, bring "a" to the front }); }) 

正如其他答案中所解释的那样,SVG没有z-索引的概念。 相反,文档中元素的顺序决定了graphics中的顺序。

除了手动对元素进行重新sorting外,对于某些情况还有另一种方法:

使用D3你经常有某些types的元素,应该总是绘制在其他types的元素之上

例如,在布置graphics时,链接应始终放置在节点下方。 更一般地说,一些背景元素通常需要放置在其他所有元素之下,而一些高亮和覆盖元素应放置在上方。

如果你有这种情况,我发现为这些元素创build父组元素是最好的方法。 在SVG中,你可以使用g元素。 例如,如果您的链接始终应位于节点下方,请执行以下操作:

 svg.append("g").attr("id", "links") svg.append("g").attr("id", "nodes") 

现在,当你画你的链接和节点时,select如下(以#开始的select器引用元素ID):

 svg.select("#links").selectAll(".link") // add data, attach elements and so on svg.select("#nodes").selectAll(".node") // add data, attach elements and so on 

现在,所有的链接将始终在所有节点元素的结构上附加。 因此,SVG将显示所有节点下的所有链接,无论添加或删除元素的次数和顺序如何。 当然,同一types的所有元素(即在同一个容器内)仍然会受到它们添加的顺序的限制。

由于SVG没有Z索引,但是使用了DOM元素的顺序,所以你可以通过以下方法把它带到前面:

 this.parentNode.appendChild(this); 

然后,您可以使用insertBefore将其放回到mouseout 。 然而这要求你能够定位你的元素应该被插入的兄弟节点。

演示:看看这个JSFiddle

SVG不做z-index。 Z顺序是由它们容器中的SVG DOM元素的顺序决定的。

据我所知(过去我已经尝试了几次),D3并没有提供分离和重新附加单个元素的方法,以便将其引入前台或其他内容。

有一个.order()方法 ,它重新组合它们出现在select中的顺序。 就你而言,你需要把一个元素放在前面。 所以,在技术上,你可以使用前面所需的元素(或最后,不记得哪个是最上面的)select,然后调用order()

或者,你可以跳过这个任务的d3,并使用普通的JS(或jQuery)重新插入单个DOM元素。

我在我的代码中实现了futurend的解决scheme,它的工作,但我用了大量的元素,这是非常缓慢的。 以下是使用jQuery的另一种方法,它可以为我的特定可视化工作更快速。 它依赖于你想在顶部有一个类共同的svgs(在我的例子中,类是在我的数据集中logging为d.key)。 在我的代码中有一个包含所有我正在重新组织的SVG的类“位置”的<g>

 .on("mouseover", function(d) { var pts = $("." + d.key).detach(); $(".locations").append(pts); }); 

因此,当您将鼠标hover在某个特定的数据点上时,代码会find所有其他具有该特定类的SVG DOM元素的数据点。 然后分离并重新插入与这些数据点关联的SVG DOM元素。

想要扩大@ notan3xit的回答,而不是写出一个全新的答案(但我没有足够的声望)。

解决元素顺序问题的另一种方法是在绘图时使用“插入”而不是“追加”。 这样,path将总是放在其他svg元素之前(假设你的代码已经在其他svg元素的enter()之前执行了enter()的链接)。

d3插入api: https : //github.com/mbostock/d3/wiki/Selections#insert

花了我很长时间才发现如何在现有的SVG中调整Z顺序。 我真的需要它在与工具提示行为d3.brush的上下文。 为了使这两个function很好地结合在一起( http://wrobstory.github.io/2013/11/D3-brush-and-tooltip.html ),您需要d3.brush成为Z-order中的第一个(第一个在canvas上绘制,然后由其余的SVG元素覆盖),它将捕捉所有的鼠标事件,不pipe它在顶部(Z指数较高)。

大多数论坛评论说,你应该先在代码中添加d3.brush,然后是你的SVG“绘图”代码。 但对我来说这是不可能的,因为我加载了一个外部的SVG文件。 您可以随时轻松添加画笔,并稍后改变Z顺序:

 d3.select("svg").insert("g", ":first-child"); 

在d3.brush安装的环境中,它将如下所示:

 brush = d3.svg.brush() .x(d3.scale.identity().domain([1, width-1])) .y(d3.scale.identity().domain([1, height-1])) .clamp([true,true]) .on("brush", function() { var extent = d3.event.target.extent(); ... }); d3.select("svg").insert("g", ":first-child"); .attr("class", "brush") .call(brush); 

d3.js insert()函数API: https : //github.com/mbostock/d3/wiki/Selections#insert

希望这可以帮助!

简单的答案是使用d3sorting方法。 除了d3.select('g')。order(),在版本4中还有.lower()和.raise()。这改变了你的元素的显示方式。 请参阅文档以获取更多信息 – https://github.com/d3/d3/blob/master/API.md#selections-d3-selection

版本1

从理论上讲,下面应该可以正常工作。

CSS代码:

 path:hover { stroke: #fff; stroke-width : 2; } 

这个CSS代码将为选定的path添加一个描边。

JS代码:

 svg.selectAll("path").on("mouseover", function(d) { this.parentNode.appendChild(this); }); 

此JS代码首先从DOM树中删除path,然后将其添加为其父项的最后一个子项。 这确保path绘制在同一父级的所有其他子级之上。

实际上,这段代码在Chrome中可以正常工作,但在其他一些浏览器中却可以使用。 我在我的Linux Mint机器上的Firefox 20上试过了,无法启动。 不知何故,Firefox无法触发:hover样式,我还没有find解决这个问题的方法。


版本2

所以我想出了一个替代scheme。 它可能有点“脏”,但至less它工作,并不需要循环所有元素(作为其他答案)。

CSS代码:

 path.hover { stroke: #fff; stroke-width : 2; } 

而不是使用:hover假select器,我使用.hover

JS代码:

 svg.selectAll(".path") .on("mouseover", function(d) { d3.select(this).classed('hover', true); this.parentNode.appendChild(this); }) .on("mouseout", function(d) { d3.select(this).classed('hover', false); }) 

在mouseover上,我将.hover类添加到我的path中。 在鼠标移出时,我将其移除。 与第一种情况一样,代码也从DOM树中移除path,然后将其添加为其父项的最后一个子项。

你可以这样做在鼠标hover你可以把它拉到顶部。

 d3.selection.prototype.bringElementAsTopLayer = function() { return this.each(function(){ this.parentNode.appendChild(this); }); }; d3.selection.prototype.pushElementAsBackLayer = function() { return this.each(function() { var firstChild = this.parentNode.firstChild; if (firstChild) { this.parentNode.insertBefore(this, firstChild); } }); 

};

 nodes.on("mouseover",function(){ d3.select(this).bringElementAsTopLayer(); }); 

如果你想推回来

 nodes.on("mouseout",function(){ d3.select(this).pushElementAsBackLayer(); });