Was ist ein effektiver Weg, um ein SVG-Element mithilfe der D3-Bibliothek an die Spitze der Z-Ordnung zu bringen?
Mein spezielles Szenario ist ein Kreisdiagramm, das hervorhebt (durch Hinzufügen eines stroke
zu path
), wenn sich die Maus über einem bestimmten Teil befindet. Der Codeblock zum Generieren meines Diagramms ist unten:
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);
});
Ich habe ein paar Optionen ausprobiert, aber bisher kein Glück. Mit style("z-index")
und ruft classed
beide nicht funktioniert hat .
Die "Top" -Klasse ist in meinem CSS wie folgt definiert:
.top {
fill: red;
z-index: 100;
}
Die fill
Aussage soll sicherstellen, dass ich wusste, dass sie richtig ein- und ausgeschaltet wurde. Es ist.
Ich habe gehört, dass die Verwendung sort
eine Option ist, aber ich bin mir nicht sicher, wie sie implementiert werden soll, um das "ausgewählte" Element nach oben zu bringen.
AKTUALISIEREN:
Ich habe meine spezielle Situation mit dem folgenden Code behoben, der der SVG des mouseover
Ereignisses einen neuen Bogen hinzufügt , um ein Highlight anzuzeigen.
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();
});
quelle
a.id
und nicht funktioniertd.id
. Die Lösung besteht darin,a
und direkt zu vergleichend
.<g>
.svg:use
Element dynamisch auf das erhabene Element zeigen. Eine Lösung finden Sie unter stackoverflow.com/a/6289809/292085 .Wie in den anderen Antworten erläutert, hat SVG keine Vorstellung von einem Z-Index. Stattdessen bestimmt die Reihenfolge der Elemente im Dokument die Reihenfolge in der Zeichnung.
Abgesehen von der manuellen Neuanordnung der Elemente gibt es für bestimmte Situationen eine andere Möglichkeit:
Bei der Arbeit mit D3 gibt es häufig bestimmte Elementtypen, die immer über anderen Elementtypen gezeichnet werden sollten .
Beispielsweise sollten beim Erstellen von Diagrammen Verknüpfungen immer unter Knoten platziert werden. Im Allgemeinen müssen einige Hintergrundelemente normalerweise unter allem anderen platziert werden, während einige Hervorhebungen und Überlagerungen darüber platziert werden sollten.
Wenn Sie in einer solchen Situation sind, habe ich festgestellt, dass das Erstellen übergeordneter Gruppenelemente für diese Elementgruppen der beste Weg ist. In SVG können Sie das
g
Element dafür verwenden. Wenn Sie beispielsweise Links haben, die immer unter Knoten platziert werden sollten, gehen Sie wie folgt vor:svg.append("g").attr("id", "links") svg.append("g").attr("id", "nodes")
Wenn Sie nun Ihre Links und Knoten malen, wählen Sie Folgendes aus (die Selektoren beginnen mit der
#
Referenz der Element-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
Jetzt werden alle Links immer strukturell vor allen Knotenelementen angehängt. Somit zeigt die SVG alle Links unter allen Knoten an, egal wie oft und in welcher Reihenfolge Sie Elemente hinzufügen oder entfernen. Natürlich unterliegen alle Elemente desselben Typs (dh innerhalb desselben Containers) weiterhin der Reihenfolge, in der sie hinzugefügt wurden.
quelle
Da SVG keinen Z-Index hat, sondern die Reihenfolge der DOM-Elemente verwendet, können Sie ihn nach vorne bringen, indem Sie:
this.parentNode.appendChild(this);
Sie können dann zB insertBefore verwenden , um es wieder anzulegen
mouseout
. Dies setzt jedoch voraus, dass Sie in der Lage sind, den Geschwisterknoten anzuvisieren, in den Ihr Element zuvor eingefügt werden soll.DEMO: Sehen Sie sich diese JSFiddle an
quelle
:hover
Stile rendern könnte . Ich denke auch nicht, dass die Einfügefunktion am Ende für diesen Anwendungsfall erforderlich wäre.insert
Funktion von d3 wahrscheinlich nicht der beste Weg ist, weil sie ein neues Element erzeugt. Wir möchten nur ein vorhandenes Element verschieben, daher verwende ichinsertBefore
stattdessen in diesem Beispiel.SVG führt keinen Z-Index durch. Die Z-Reihenfolge wird durch die Reihenfolge der SVG-DOM-Elemente in ihrem Container bestimmt.
Soweit ich das beurteilen konnte (und ich habe dies in der Vergangenheit einige Male versucht), bietet D3 keine Methoden zum Trennen und erneuten Anbringen eines einzelnen Elements, um es nach vorne zu bringen oder so weiter.
Es gibt eine
.order()
Methode , mit der die Knoten neu gemischt werden, um der Reihenfolge zu entsprechen, in der sie in der Auswahl angezeigt werden. In Ihrem Fall müssen Sie ein einzelnes Element nach vorne bringen. Technisch gesehen können Sie also die Auswahl mit dem gewünschten Element im Vordergrund neu positionieren (oder sich am Ende nicht erinnern, welches das oberste ist) und es dann aufrufenorder()
.Sie können auch d3 für diese Aufgabe überspringen und einfaches JS (oder jQuery) verwenden, um das einzelne DOM-Element erneut einzufügen.
quelle
.style('pointer-events', 'none')
den überlagerten Pfad hinzufügen . Leider macht diese Eigenschaft nichts im IE, aber immer noch ....Die einfache Antwort ist die Verwendung von d3-Bestellmethoden. Zusätzlich zu d3.select ('g'). Order () gibt es in Version 4 .lower () und .raise (). Dies ändert die Darstellung Ihrer Elemente. Weitere Informationen finden Sie in den Dokumenten unter https://github.com/d3/d3/blob/master/API.md#selections-d3-selection
quelle
Ich habe die Lösung von futurend in meinen Code implementiert und es hat funktioniert, aber mit der großen Anzahl von Elementen, die ich verwendet habe, war es sehr langsam. Hier ist die alternative Methode mit jQuery, die für meine spezielle Visualisierung schneller funktioniert hat. Es hängt davon ab, ob die gewünschten SVGs eine gemeinsame Klasse haben (in meinem Beispiel wird die Klasse in meinem Datensatz als d.key vermerkt). In meinem Code gibt es eine
<g>
mit der Klasse "Standorte", die alle SVGs enthält, die ich neu organisiere..on("mouseover", function(d) { var pts = $("." + d.key).detach(); $(".locations").append(pts); });
Wenn Sie also mit der Maus über einen bestimmten Datenpunkt fahren, findet der Code alle anderen Datenpunkte mit SVG-DOM-Elementen mit dieser bestimmten Klasse. Anschließend werden die diesen Datenpunkten zugeordneten SVG-DOM-Elemente entfernt und wieder eingefügt.
quelle
Wollte die Antwort von @ notan3xit erweitern, anstatt eine ganz neue Antwort zu schreiben (aber ich habe nicht genug Ruf).
Eine andere Möglichkeit, das Problem der Elementreihenfolge zu lösen, besteht darin, beim Zeichnen "Einfügen" anstelle von "Anhängen" zu verwenden. Auf diese Weise werden die Pfade immer zusammen vor den anderen SVG-Elementen platziert (dies setzt voraus, dass Ihr Code bereits die Eingabe () für Links vor der Eingabe () für die anderen SVG-Elemente ausführt).
d3 API einfügen: https://github.com/mbostock/d3/wiki/Selections#insert
quelle
Ich habe ewig gebraucht, um herauszufinden, wie ich die Z-Reihenfolge in einem vorhandenen SVG optimieren kann. Ich brauchte es wirklich im Kontext von d3.brush mit Tooltip-Verhalten. Damit die beiden Funktionen gut zusammenarbeiten ( http://wrobstory.github.io/2013/11/D3-brush-and-tooltip.html ), muss der d3.brush der erste in Z-Reihenfolge sein (1. wird auf die Leinwand gezeichnet und dann von den restlichen SVG-Elementen abgedeckt) und erfasst alle Mausereignisse, unabhängig davon, was sich darüber befindet (mit höheren Z-Indizes).
Die meisten Forumkommentare besagen, dass Sie zuerst den d3.brush in Ihren Code und dann Ihren SVG-Zeichnungscode einfügen sollten. Für mich war dies jedoch nicht möglich, da ich eine externe SVG-Datei geladen habe. Sie können den Pinsel jederzeit einfach hinzufügen und die Z-Reihenfolge später ändern mit:
d3.select("svg").insert("g", ":first-child");
Im Kontext eines d3.brush-Setups sieht es so aus:
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 () - Funktions-API: https://github.com/mbostock/d3/wiki/Selections#insert
Hoffe das hilft!
quelle
Version 1
Theoretisch sollte das Folgende gut funktionieren.
Der CSS-Code:
path:hover { stroke: #fff; stroke-width : 2; }
Dieser CSS-Code fügt dem ausgewählten Pfad einen Strich hinzu.
Der JS-Code:
svg.selectAll("path").on("mouseover", function(d) { this.parentNode.appendChild(this); });
Dieser JS-Code entfernt zuerst den Pfad aus dem DOM-Baum und fügt ihn dann als letztes untergeordnetes Element seines übergeordneten Elements hinzu. Dadurch wird sichergestellt, dass der Pfad über allen anderen Kindern desselben Elternteils gezeichnet wird.
In der Praxis funktioniert dieser Code in Chrome einwandfrei, in einigen anderen Browsern jedoch nicht. Ich habe es in Firefox 20 auf meinem Linux Mint-Computer versucht und konnte es nicht zum Laufen bringen. Irgendwie kann Firefox die
:hover
Stile nicht auslösen , und ich habe keinen Weg gefunden, dies zu beheben.Version 2
Also habe ich mir eine Alternative ausgedacht. Es mag ein bisschen "schmutzig" sein, aber zumindest funktioniert es und es erfordert nicht das Schleifen aller Elemente (wie einige der anderen Antworten).
Der CSS-Code:
path.hover { stroke: #fff; stroke-width : 2; }
Anstatt den
:hover
Pseudoselektor zu verwenden, verwende ich eine.hover
KlasseDer JS-Code:
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); })
Beim Mouseover füge ich die
.hover
Klasse meinem Pfad hinzu. Beim Mouseout entferne ich es. Wie im ersten Fall entfernt der Code auch den Pfad aus dem DOM-Baum und fügt ihn dann als letztes untergeordnetes Element seines übergeordneten Elements hinzu.quelle
Sie können dies bei Mauszeiger tun. Sie können es nach oben ziehen.
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(); });
Wenn Sie nach hinten drücken möchten
nodes.on("mouseout",function(){ d3.select(this).pushElementAsBackLayer(); });
quelle