Javascript 使用 D3 更新 SVG 元素 Z-Index
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13595175/
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
Updating SVG Element Z-Index With D3
提问by Evil Closet Monkey
What is an effective way to bring an SVG element to the top of the z-order, using the D3 library?
使用 D3 库将 SVG 元素置于 z 顺序顶部的有效方法是什么?
My specific scenario is a pie chart which highlights (by adding a stroketo the path) when the mouse is over a given piece. The code block for generating my chart is below:
我的特定情况是一个饼图其中突出(通过添加stroke到path)当鼠标在给定的片。生成图表的代码块如下:
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);
});
I've tried a few options, but no luck so far. Using style("z-index")and calling classedboth did not work.
我尝试了一些选择,但到目前为止没有运气。使用style("z-index")和调用classed两者都不起作用。
The "top" class is defined as follows in my CSS:
“top”类在我的 CSS 中定义如下:
.top {
fill: red;
z-index: 100;
}
The fillstatement is there to make sure I knew it was turning on/off correctly. It is.
该fill声明是为了确保我知道它正在正确打开/关闭。这是。
I've heard using sortis an option, but I'm unclear on how it would be implemented for bringing the "selected" element to the top.
我听说 usingsort是一个选项,但我不清楚如何实现将“选定”元素带到顶部。
UPDATE:
更新:
I fixed my particular situation with the following code, which adds a new arc to the SVG on the mouseoverevent to show a highlight.
我使用以下代码修复了我的特殊情况,该代码在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();
});
采纳答案by futurend
One of the solutions presented by the developer is: "use D3's sort operator to reorder the elements." (see https://github.com/mbostock/d3/issues/252)
开发人员提出的解决方案之一是:“使用 D3 的排序运算符对元素重新排序。” (见https://github.com/mbostock/d3/issues/252)
In this light, one might sort the elements by comparing their data, or positions if they were dataless elements:
有鉴于此,可以通过比较元素的数据或位置(如果它们是无数据元素)来对元素进行排序:
.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
});
})
回答by notan3xit
As explained in the other answers, SVG does not have a notion of a z-index. Instead, the order of elements in the document determines the order in the drawing.
正如其他答案中所解释的,SVG 没有 z-index 的概念。相反,文档中元素的顺序决定了绘图中的顺序。
Apart from reordering the elements manually, there is another way for certain situations:
除了手动对元素重新排序之外,对于某些情况还有另一种方法:
Working with D3 you often have certain types of elements that should always be drawn on top of other types of elements.
使用 D3 时,您经常会遇到某些类型的元素,这些元素应始终绘制在其他类型的元素之上。
For example, when laying out graphs, links should always be placed below nodes. More generally, some background elements usually need to be placed below everything else, while some highlights and overlays should be placed above.
例如,在布置图形时,链接应始终放置在节点下方。更一般地,一些背景元素通常需要放置在其他所有元素的下方,而一些高光和叠加层应该放置在上方。
If you have this kind of situation, I found that creating parent group elementsfor those groups of elements is the best way to go. In SVG, you can use the gelement for that. For example, if you have links that should be always placed below nodes, do the following:
如果您遇到这种情况,我发现为这些元素组创建父组元素是最好的方法。在 SVG 中,您可以使用该g元素。例如,如果您的链接应始终放置在节点下方,请执行以下操作:
svg.append("g").attr("id", "links")
svg.append("g").attr("id", "nodes")
Now, when you paint your links and nodes, select as follows (the selectors starting with #reference the element id):
现在,当您绘制链接和节点时,请按如下方式选择(选择器以#引用元素 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
Now, all links will always be appended structurally before all node elements. Thus, the SVG will show all links below all nodes, no matter how often and in what order you add or remove elements. Of course, all elements of the same type (i.e. within the same container) will still be subject to the order in which they were added.
现在,所有链接将始终在结构上附加在所有节点元素之前。因此,SVG 将显示所有节点下方的所有链接,无论您添加或删除元素的频率和顺序如何。当然,所有相同类型的元素(即在同一个容器内)仍将遵循它们添加的顺序。
回答by swenedo
Since SVG doesn't have Z-index but use the order of the DOM elements, you can bring it to front by:
由于 SVG 没有 Z-index 而是使用 DOM 元素的顺序,因此您可以通过以下方式将其置于最前面:
this.parentNode.appendChild(this);
You can then e.g. make use of insertBeforeto put it back on mouseout. This however requires you to be able to target the sibling-node your element should be inserted before.
然后您可以例如使用insertBefore将其放回mouseout。然而,这要求您能够定位您的元素之前应该插入的兄弟节点。
DEMO:Take a look at this JSFiddle
演示:看看这个JSFiddle
回答by meetamit
SVG doesn't do z-index. Z-order is dictated by the order of the SVG DOM elements in their container.
SVG 不做 z-index。Z-order 由其容器中 SVG DOM 元素的顺序决定。
As far as I could tell (and I've tried this a couple of times in the past), D3 doesn't provide methods for detaching and reattaching a single element in order to bring it to the front or whatnot.
据我所知(我过去曾尝试过几次),D3 不提供分离和重新附加单个元素以将其置于前面或诸如此类的方法。
There is an .order()method, which reshuffles the nodes to match the order they appear in the selection. In your case, you need to bring a single element to the front. So, technically, you could resort the selection with the desired element in front (or at the end, can't remember which is topmost), and then call order()on it.
有一个.order()方法,它重新排列节点以匹配它们在选择中出现的顺序。在您的情况下,您需要将单个元素放在前面。因此,从技术上讲,您可以将所需元素放在前面(或在最后,不记得哪个是最上面的),然后调用order()它。
Or, you could skip d3 for this task and use plain JS (or jQuery) to re-insert that single DOM element.
或者,您可以跳过此任务的 d3 并使用纯 JS(或 jQuery)重新插入该单个 DOM 元素。
回答by evanjmg
The simple answer is to use d3 ordering methods. In addition to d3.select('g').order(), there is .lower() and .raise() in version 4. This changes how your elements appear. Please consult the docs for more information - https://github.com/d3/d3/blob/master/API.md#selections-d3-selection
简单的答案是使用 d3 排序方法。除了 d3.select('g').order() 之外,版本 4 中还有 .lower() 和 .raise()。这会改变元素的显示方式。请查阅文档以获取更多信息 - https://github.com/d3/d3/blob/master/API.md#selections-d3-selection
回答by lk145
I implemented futurend's solution in my code and it worked, but with the large number of elements I was using, it was very slow. Here's the alternative method using jQuery that worked faster for my particular visualization. It relies on the svgs you want on top having a class in common (in my example the class is noted in my data set as d.key). In my code there is a <g>with the class "locations" that contains all of the SVGs I'm re-organizing.
我在我的代码中实现了 futurend 的解决方案并且它起作用了,但是由于我使用了大量元素,它非常慢。这是使用 jQuery 的替代方法,它对我的特定可视化效果更快。它依赖于你想要的 svgs 上面有一个共同的类(在我的例子中,这个类在我的数据集中被标记为 d.key)。在我的代码中,有一个<g>类“locations”,其中包含我正在重新组织的所有 SVG。
.on("mouseover", function(d) {
var pts = $("." + d.key).detach();
$(".locations").append(pts);
});
So when you hover on a particular data point, the code finds all the other data points with SVG DOM elements with that particular class. Then it detaches and re-inserts the SVG DOM elements associated with those data points.
因此,当您将鼠标悬停在特定数据点上时,代码会查找具有该特定类的 SVG DOM 元素的所有其他数据点。然后它分离并重新插入与这些数据点关联的 SVG DOM 元素。
回答by Ali
Wanted to expand on what @notan3xit answered rather than write out an entire new answer (but I don't have enough reputation).
想扩展@notan3xit 的回答,而不是写出一个全新的答案(但我没有足够的声誉)。
Another way to solve the element order problem is to use 'insert' rather than 'append' when drawing . That way the paths will always be placed together before the other svg elements(this assumes your code already does the enter() for links before the enter() for the other svg elements).
解决元素顺序问题的另一种方法是在绘制时使用“插入”而不是“附加”。这样,路径将始终放在其他 svg 元素之前(假设您的代码已经在其他 svg 元素的 enter() 之前为链接执行了 enter() 操作)。
d3 insert api: https://github.com/mbostock/d3/wiki/Selections#insert
d3 插入 api: https://github.com/mbostock/d3/wiki/Selections#insert
回答by mskr182
It took me ages to find how to tweak the Z-order in an existing SVG. I really needed it in the context of d3.brush with tooltip behavior. In order to have the two features work nicely together (http://wrobstory.github.io/2013/11/D3-brush-and-tooltip.html), you need the d3.brush to be the first in Z-order (1st to be drawn on the canvas, then covered by the rest of the SVG elements) and it will capture all mouse events, no matter what is on top of it (with higher Z indices).
我花了很长时间才找到如何调整现有 SVG 中的 Z 顺序。在带有工具提示行为的 d3.brush 上下文中,我真的需要它。为了让这两个功能很好地协同工作(http://wrobstory.github.io/2013/11/D3-brush-and-tooltip.html),您需要 d3.brush 成为 Z 顺序中的第一个(第一个在画布上绘制,然后被其余的 SVG 元素覆盖)它将捕获所有鼠标事件,无论它上面是什么(具有更高的 Z 索引)。
Most forum comments say that you should add the d3.brush first in your code, then your SVG "drawing" code. But for me it was not possible as I loaded an external SVG file. You can easily add the brush at any time and alter the Z-order later on with:
大多数论坛评论都说您应该首先在代码中添加 d3.brush,然后是 SVG“绘图”代码。但对我来说这是不可能的,因为我加载了一个外部 SVG 文件。您可以随时轻松添加画笔并稍后更改 Z 顺序:
d3.select("svg").insert("g", ":first-child");
In the context of a d3.brush setup it will look like:
在 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() function API: https://github.com/mbostock/d3/wiki/Selections#insert
d3.js insert() 函数 API:https: //github.com/mbostock/d3/wiki/Selections#insert
Hope this helps!
希望这可以帮助!
回答by John Slegers
Version 1
版本 1
In theory, the following should work fine.
从理论上讲,以下应该可以正常工作。
The CSS code :
CSS 代码:
path:hover {
stroke: #fff;
stroke-width : 2;
}
This CSS code will add a stroke to the selected path.
此 CSS 代码将向所选路径添加笔划。
The JS code :
JS代码:
svg.selectAll("path").on("mouseover", function(d) {
this.parentNode.appendChild(this);
});
This JS code first removes the path from the DOM tree and then adds it as the last child of its parent. This makes sure the path is drawn on top of all other children of the same parent.
此 JS 代码首先从 DOM 树中删除路径,然后将其添加为其父项的最后一个子项。这确保路径绘制在同一父级的所有其他子级之上。
In practice, this code works fine in Chrome but breaks in some other browsers. I tried it in Firefox 20 on my Linux Mint machine and couldn't get it to work. Somehow, Firefox fails to trigger the :hoverstyles and I haven't found a way to fix this.
在实践中,这段代码在 Chrome 中运行良好,但在其他一些浏览器中会中断。我在 Linux Mint 机器上的 Firefox 20 中尝试了它,但无法让它工作。不知何故,Firefox 无法触发:hover样式,我还没有找到解决此问题的方法。
Version 2
版本 2
So I came up with an alternative. It may be a bit 'dirty', but at least it works and it doesn't require looping over all elements (as some of the other answers).
所以我想出了一个替代方案。它可能有点“脏”,但至少它可以工作并且不需要循环遍历所有元素(如其他一些答案)。
The CSS code :
CSS 代码:
path.hover {
stroke: #fff;
stroke-width : 2;
}
Instead of using the :hoverpseudoselector, I use a .hoverclass
而不是使用的:hoverpseudoselector,我使用的是.hover类
The JS code :
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);
})
On mouseover, I add the .hoverclass to my path. On mouseout, I remove it.
As in the first case, the code also removes the path from the DOM tree and then adds it as the last child of its parent.
在鼠标悬停时,我将.hover类添加到我的路径中。在鼠标移出时,我将其删除。与第一种情况一样,代码也从 DOM 树中删除了路径,然后将其添加为其父级的最后一个子级。
回答by RamiReddy P
You can Do like this On Mouse Over You can Pull it to top.
你可以在鼠标悬停时这样做 你可以把它拉到顶部。
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();
});
If You want To Push To Back
如果你想推回
nodes.on("mouseout",function(){
d3.select(this).pushElementAsBackLayer();
});

