it-swarm.com.de

Aktualisieren des SVG-Elements Z-Index mit D3

Wie kann ein SVG-Element mithilfe der D3-Bibliothek effektiv an die Spitze der z-Reihenfolge gebracht werden?

Mein spezielles Szenario ist ein Kreisdiagramm, das hervorhebt (durch Hinzufügen einer stroke zur path), wenn sich der Mauszeiger über einem bestimmten Stück befindet. Der Codeblock zum Generieren meines Diagramms befindet sich 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. Die Verwendung von style("z-index") und dem Aufruf von classed funktionierte nicht.

Die "top" -Klasse ist in meinem CSS folgendermaßen definiert:

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

Die fill-Anweisung soll sicherstellen, dass ich weiß, dass sie richtig ein-/ausgeschaltet wurde. Es ist.

Ich habe gehört, dass sort eine Option ist, aber ich bin unklar, wie sie implementiert werden würde, um das "ausgewählte" Element nach oben zu bringen.

UPDATE:

Ich habe meine spezielle Situation mit dem folgenden Code behoben, der dem SVG einen neuen Bogen für das Ereignis mouseover 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();
    });
67

Eine der vom Entwickler vorgestellten Lösungen lautet: "Verwenden Sie den Sortieroperator von D3, um die Elemente neu anzuordnen." (siehe https://github.com/mbostock/d3/issues/252 )

In diesem Licht könnte man die Elemente sortieren, indem man ihre Daten oder Positionen vergleicht, wenn sie datenlose Elemente sind:

.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
  });
})
46
futurend

Wie in den anderen Antworten erläutert, hat SVG keine Vorstellung von einem Z-Index. Die Reihenfolge der Elemente im Dokument bestimmt stattdessen die Reihenfolge in der Zeichnung.

Neben der manuellen Neuordnung der Elemente gibt es für bestimmte Situationen eine andere Möglichkeit:

Beim Arbeiten mit D3 haben Sie oft bestimmte Elementtypen, die immer über anderen Elementtypen gezeichnet werden sollten .

Beim Erstellen von Diagrammen sollten Links beispielsweise immer unter Knoten platziert werden. Im Allgemeinen müssen einige Hintergrundelemente normalerweise unter allen anderen platziert werden, während einige Hervorhebungen und Überlagerungen darüber platziert werden sollten.

Wenn Sie diese Situation haben, habe ich festgestellt, dass das Erstellen von übergeordnete Gruppenelemente für diese Elementgruppen der beste Weg ist. In SVG können Sie dazu das Element g verwenden. Wenn Sie beispielsweise Verknüpfungen haben, die immer unter Knoten platziert werden sollen, gehen Sie wie folgt vor:

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

Wenn Sie nun Ihre Links und Knoten zeichnen, wählen Sie wie folgt aus (die Selektoren, die mit # beginnen, verweisen auf die 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 Verknüpfungen vor allen Knotenelementen strukturell angehängt. Daher zeigt die SVG alle Links unter allen Knoten an, unabhängig davon, wie oft und in welcher Reihenfolge Sie Elemente hinzufügen oder entfernen. Natürlich unterliegen alle Elemente desselben Typs (d. H. Innerhalb desselben Behälters) immer noch der Reihenfolge, in der sie hinzugefügt wurden.

81
notan3xit

Da SVG keinen Z-Index hat, sondern die Reihenfolge der DOM-Elemente verwendet, können Sie es nach vorne bringen, indem Sie:

this.parentNode.appendChild(this);

Sie können dann z. Verwenden Sie insertBefore , um es wieder auf mouseout zu setzen. Dies setzt jedoch voraus, dass Sie in der Lage sind, den Geschwisterknoten als Ziel festzulegen, vor dem Ihr Element eingefügt werden soll.

DEMO: Schauen Sie sich das an JSFiddle

20
swenedo

SVG macht keinen Z-Index. Die Z-Reihenfolge wird durch die Reihenfolge der SVG-DOM-Elemente in ihrem Container bestimmt.

Soweit ich das beurteilen konnte (und ich habe das schon ein paar Mal ausprobiert), bietet D3 keine Methoden, um ein einzelnes Element abzutrennen und wieder anzuhängen, um es nach vorne zu bringen. 

Es gibt eine .order()-Methode , bei der die Knoten in der Reihenfolge neu angeordnet werden, in der sie in der Auswahl erscheinen. 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 nach vorne verschieben (oder am Ende nicht wissen, welches oberste Element sich befindet) und dann order() darauf aufrufen.

Oder Sie können d3 für diese Aufgabe überspringen und JS (oder jQuery) verwenden, um dieses einzelne DOM-Element erneut einzufügen.

13
meetamit

Die einfache Antwort ist die Verwendung von d3-Bestellmethoden. Neben 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 - https://github.com/d3/d3/blob/master/API.md#selections-d3-selection .

6
evanjmg

Wollte erweitern, was @ notan3xit geantwortet hat, anstatt eine komplette 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 vor den anderen svg-Elementen platziert (dies setzt voraus, dass Ihr Code bereits das enter () für Links vor dem enter () für die anderen svg-Elemente übernimmt).

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

4
Ali

Ich habe die Lösung von futurend in meinem Code implementiert und es hat funktioniert, aber mit der großen Anzahl von Elementen, die ich verwendete, war es sehr langsam. Hier ist die alternative Methode mit jQuery, die für meine spezielle Visualisierung schneller funktionierte. Es setzt voraus, dass die svgs, die Sie wollen, eine gemeinsame Klasse haben (in meinem Beispiel wird die Klasse in meinem Datensatz als d.key angegeben). In meinem Code gibt es einen <g> mit der Klasse "locations", die alle SVGs enthält, die ich neu organisiere.

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

Wenn Sie sich also auf einem bestimmten Datenpunkt befinden, findet der Code alle anderen Datenpunkte mit SVG-DOM-Elementen mit dieser bestimmten Klasse. Dann werden die mit diesen Datenpunkten verknüpften SVG-DOM-Elemente entfernt und wieder eingefügt.

4
lk145

Ich brauchte eine Ewigkeit, um herauszufinden, wie man die Z-Ordnung 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 ), benötigen Sie die erste d3.brush in Z-Reihenfolge (1. wird auf der Leinwand gezeichnet, dann durch die restlichen SVG-Elemente abgedeckt) und erfasst alle Mausereignisse, unabhängig davon, was sich darauf befindet (mit höheren Z-Indizes).

Die meisten Forum-Kommentare besagen, dass Sie zunächst d3.brush in Ihren Code und dann Ihren SVG-Zeichnungscode hinzufü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 problemlos 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 folgendermaßen 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!

1
mskr182

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 zunächst den Pfad aus der DOM-Struktur und fügt ihn dann als letztes untergeordnetes Element seines übergeordneten Elements hinzu. Dadurch wird sichergestellt, dass der Pfad über alle anderen untergeordneten Elemente desselben übergeordneten Elements gezeichnet wird.

In der Praxis funktioniert dieser Code in Chrome einwandfrei, unterbricht jedoch einige andere Browser. Ich habe es in Firefox 20 auf meiner Linux Mint-Maschine ausprobiert und konnte es nicht zum Laufen bringen. Irgendwie löst Firefox die :hover-Stile nicht aus und ich habe keine Möglichkeit gefunden, dies zu beheben.


Version 2

Also habe ich mir eine Alternative ausgedacht. Es mag etwas "schmutzig" sein, aber es funktioniert zumindest und es ist nicht erforderlich, alle Elemente zu durchlaufen (wie in den anderen Antworten).

Der CSS-Code:

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

Anstelle der :hover-Pseudoselector verwende ich eine .hover-Klasse

Der 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 meinem Pfad die .hover-Klasse hinzu. Bei mouseout entferne ich es . Wie im ersten Fall entfernt der Code auch den Pfad aus der DOM-Struktur und fügt ihn dann als letztes untergeordnetes Element seines übergeordneten Elements hinzu.

0
John Slegers

Sie können dies auch mit der Maus tun.

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();
});
0
RamiReddy P