Mise à jour de L'élément SVG Z-Index par D3

Quel est le moyen efficace pour amener un élément SVG au sommet de la Z-order, en utilisant la bibliothèque D3?

mon scénario spécifique est un diagramme à secteurs qui met en évidence (en ajoutant un stroke au path ) quand la souris est sur une pièce Donnée. Le bloc de code pour générer mon diagramme est ci-dessous:

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);
    });

j'ai essayé quelques options, mais pas de chance jusqu'à présent. L'utilisation de style("z-index") et l'appel de classed n'ont pas fonctionné.

la classe" supérieure "est définie comme suit dans mon CSS:

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

la déclaration fill est là pour m'assurer que je savais qu'elle s'allumait/s'éteignait correctement. Il est.

j'ai entendu dire que l'utilisation de sort est une option, mais je ne sais pas comment elle serait implémentée pour amener l'élément" selected " au sommet.

mise à jour:

j'ai réparé mon situation particulière avec le code suivant, qui ajoute un nouvel arc à la SVG sur l'événement mouseover pour afficher un highlight.

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();
    });
64
demandé sur Evil Closet Monkey 2012-11-28 03:34:38

10 réponses

L'une des solutions présentées par le développeur est:" utilisez L'opérateur de tri de D3 pour réorganiser les éléments."(voir https://github.com/mbostock/d3/issues/252 )

dans cette optique, on pourrait trier les éléments en comparant leurs données, ou positions s'il s'agissait d'éléments sans données:

.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
  });
})
45
répondu futurend 2013-08-22 08:22:35

comme expliqué dans les autres réponses, SVG n'a pas la notion de z-index. Au lieu de cela, l'ordre des éléments dans le document détermine l'ordre dans le dessin.

outre la réorganisation manuelle des éléments, il y a une autre façon pour certaines situations:

Travailler avec D3-vous souvent de certains types d'éléments qui devraient toujours être dessiné sur d'autres types d'éléments .

pour par exemple, lors de la présentation des graphiques, les liens doivent toujours être placés sous les noeuds. Plus généralement, certains éléments de fond doivent être placés sous tout le reste, tandis que certains points saillants et les superpositions doivent être placés au-dessus.

si vous avez ce genre de situation, j'ai trouvé que la création de éléments du groupe parent pour ces groupes d'éléments est la meilleure façon d'aller. En SVG, vous pouvez utiliser l'élément g pour cela. Par exemple, si vous avez des liens qui devrait toujours être placé sous les noeuds, faire ce qui suit:

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

maintenant, lorsque vous peignez vos liens et noeuds, sélectionnez comme suit (les sélecteurs commençant par # référence l'élément 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

maintenant, tous les liens seront toujours ajoutés structurellement avant tous les éléments de noeud. Ainsi, la SVG affichera tous les liens sous tous les noeuds, peu importe la fréquence et l'ordre dans lequel vous ajoutez ou supprimez des éléments. Bien sûr, tous les éléments de le même type (c'est à dire dans le même conteneur) seront toujours soumises à l'ordre dans lequel ils ont été ajoutés.

81
répondu notan3xit 2013-06-18 22:17:36

puisque SVG n'a pas de Z-index mais utilise l'ordre des éléments DOM, vous pouvez le mettre à l'avant par:

this.parentNode.appendChild(this);

vous pouvez alors par exemple utiliser avant pour le remettre sur mouseout . Cela nécessite cependant que vous soyez en mesure de cibler le noeud frère que votre élément devrait être inséré avant.

Démo: regardez ce JSFiddle

19
répondu swenedo 2015-07-05 08:45:01

SVG ne fait pas de z-index. L'ordre Z est dicté par l'ordre des éléments SVG DOM dans leur conteneur.

pour autant que je sache (et j'ai essayé cela quelques fois dans le passé), D3 ne fournit pas de méthodes pour détacher et rattacher un seul élément afin de l'amener à l'avant ou autre.

il y a une .order() méthode , qui modifie les noeuds pour correspondre à l'ordre qu'ils apparaissent dans le sélection. Dans votre cas, vous devez apporter un seul élément à l'avant. Donc, techniquement, vous pourriez recourir à la sélection avec l'élément désiré en avant (ou à la fin, ne peut pas se souvenir qui est le plus haut), puis appeler order() sur elle.

ou, vous pouvez sauter d3 pour cette tâche et utiliser JS simple (ou jQuery) pour réinsérer cet élément DOM unique.

13
répondu meetamit 2013-12-09 21:33:20

j'ai implémenté la solution de futurend dans mon code et cela a fonctionné, mais avec le grand nombre d'éléments que j'utilisais, c'était très lent. Voici la méthode alternative utilisant jQuery qui a fonctionné plus rapidement pour ma visualisation particulière. Il s'appuie sur les SVG que vous voulez en haut d'avoir une classe en commun (dans mon exemple la classe est notée dans mon ensemble de données comme D. clé). Dans mon code il y a un <g> avec la classe" localisations " qui contient tous les SVG que je réorganise.

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

ainsi, lorsque vous survolez un point de données particulier, le code trouve tous les autres points de données avec des éléments SVG DOM avec cette classe particulière. Puis il détache et ré-insère les éléments SVG DOM associés à ces points de données.

4
répondu lk145 2013-06-21 23:43:47

a voulu développer ce que @notan3xit a répondu plutôt que d'écrire une nouvelle réponse entière (mais je n'ai pas assez de réputation).

une autre façon de résoudre le problème de l'ordre des éléments est d'utiliser 'insert' plutôt que 'append' lors du dessin . De cette façon, les chemins seront toujours placés ensemble avant les autres éléments svg(cela suppose que votre code fait déjà enter() pour les liens avant enter() pour les autres éléments svg).

D3 insérer api: https://github.com/mbostock/d3/wiki/Selections#insert

4
répondu Ali 2014-04-10 16:27:35

la réponse simple est d'utiliser les méthodes de commande d3. En plus de d3.sélectionnez ('g').de commande(), il y est .lower() et .raise() dans la version 4. Cela change la façon dont vos éléments apparaissent. S'il vous plaît consulter le docs pour plus d'information - https://github.com/d3/d3/blob/master/API.md#selections-d3-selection

3
répondu evanjmg 2017-02-02 10:54:11

il m'a fallu longtemps pour trouver comment modifier L'ordre Z dans un SVG existant. J'en avais vraiment besoin dans le contexte de la d3.brosse avec info-bulle comportement. Afin que les deux caractéristiques fonctionnent bien ensemble ( http://wrobstory.github.io/2013/11/D3-brush-and-tooltip.html ), vous avez besoin du d3.pinceau sera le premier en Z-order (1er à être dessiné sur la toile, puis couvert par le reste des éléments SVG) et il capturera tous les événements de la souris, peu importe ce qui est au-dessus de lui (avec indices Z plus élevés).

la plupart des commentaires du forum indiquent que vous devriez ajouter le d3.brossez d'abord votre code, puis votre SVG "dessin" code. Mais pour moi, ce n'était pas possible car j'ai chargé un fichier SVG externe. Vous pouvez facilement ajouter le pinceau à tout moment et modifier l'ordre en Z plus tard avec:

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

Dans le contexte d'un d3.la configuration du pinceau ressemblera à:

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() fonction API: https://github.com/mbostock/d3/wiki/Selections#insert

Espérons que cette aide!

1
répondu mskr182 2014-10-11 02:31:19

Version 1

En théorie, la suivante devrait fonctionner correctement.

le code CSS:

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

ce code CSS ajoutera un trait au chemin sélectionné.

le code JS:

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

ce code JS supprime d'abord le chemin de L'arbre DOM puis l'ajoute comme dernier enfant de son parent. Cela garantit que le chemin est dessiné sur le dessus de tous les autres enfants de la même parent.

en pratique, ce code fonctionne très bien dans Chrome, mais se casse dans certains autres navigateurs. Je l'ai essayé dans Firefox 20 sur ma machine Linux Mint et je n'ai pas pu le faire fonctionner. D'une façon ou d'une autre, Firefox ne parvient pas à déclencher les styles :hover et je n'ai pas trouvé le moyen de corriger cela.


Version 2

alors j'ai trouvé une alternative. Il peut être un peu "sale", mais au moins il fonctionne, et il ne nécessite pas de boucle sur tous les éléments (comme certains des autres réponses).

le code CSS:

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

au lieu d'utiliser le pseudo-sélecteur :hover , j'utilise un .hover classe

le 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);
   })

sur mouseover, j'ajoute la classe .hover à mon chemin. Sur mouseout, Je l'enlève. Comme dans le premier cas, le code supprime aussi le chemin de L'arbre DOM puis l'ajoute comme le dernier enfant de son parent.

0
répondu John Slegers 2016-02-25 16:19:02

vous pouvez faire comme cela à la souris sur vous pouvez tirer vers le haut.

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

Si Vous voulez Pousser À Dos

nodes.on("mouseout",function(){
   d3.select(this).pushElementAsBackLayer();
});
0
répondu RamiReddy P 2017-03-24 11:10:34