Aggiornamento SVG Elemento di Z-Index Con D3

Che cosa è un modo efficace per portare un elemento SVG per il top di ordine z, utilizzando la libreria D3?

Mio specifico scenario è un grafico a torta che mette in evidenza (con l’aggiunta di un stroke per il path) quando il mouse è sopra un dato pezzo. Il blocco di codice per la generazione di mio grafico è riportato di seguito:

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

Ho provato un paio di opzioni, ma senza fortuna finora. Utilizzando style("z-index") e chiamando classed sia non ha funzionato.

Il “top” class è definito come segue nel mio CSS:

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

Il fill istruzione è lì per assicurarsi sapevo che era di accendere/spegnere correttamente. È.

Ho sentito che utilizza sort è un’opzione, ma io sono poco chiare su come sarebbe stato attuato per portare la “selezionato” elemento al top.

AGGIORNAMENTO:

Ho risolto la mia situazione particolare con il codice riportato di seguito, che aggiunge un nuovo arco per il SVG sul mouseover evento per mostrare un punto culminante.

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



10 Replies
  1. 48

    Una delle soluzioni presentate dal committente è: “l’uso D3 operatore di ordinamento per riordinare gli elementi.” (vedere https://github.com/mbostock/d3/issues/252)

    In questa luce, si potrebbe ordinare gli elementi, mettendo a confronto i dati, o posizioni se fossero fisso elementi:

    .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
      });
    })
    • Questo funziona non solo su ordine Z, anche non sconvolgere qualsiasi trascinamento che ha appena iniziato con l’elemento di essere sollevato.
    • non funziona per me. a,b sembra essere solo i dati, non la d3 selezione dell’elemento o dell’elemento del DOM
    • Infatti, come per @nkint non essere quando si confrontano a.id e d.id. La soluzione è quello di confrontare direttamente a e d.
    • Questo non svolgere in modo adeguato per un gran numero di elementi figlio. Meglio re-rendering sollevato elemento in una successiva <g>.
    • In alternativa, il svg:use elemento può puntare in modo dinamico sollevata elemento. Vedere stackoverflow.com/a/6289809/292085 per la soluzione.
    • questo ucciderà qualsiasi mouseout eventi in IE

  2. 84

    Come spiegato in altre risposte, SVG non hanno una nozione di un z-index. Invece, l’ordine degli elementi nel documento determina l’ordine di disegno.

    Oltre a riordinare gli elementi manualmente, c’è un altro modo per determinate situazioni:

    Di lavoro con la D3 si hanno spesso alcuni tipi di elementi che dovrebbero sempre essere disegnato al di sopra di altri tipi di elementi.

    Per esempio, layout grafici, collegamenti devono essere sempre collocati al di sotto di nodi. Più in generale, alcuni elementi di sfondo, di solito bisogno di essere messo sotto tutto il resto, mentre alcuni dati di sintesi e sovrapposizioni deve essere posto al di sopra.

    Se si dispone di questo tipo di situazione, ho trovato che la creazione di gruppo padre elementi per quei gruppi di elementi è il modo migliore per andare. In SVG, è possibile utilizzare il g elemento. Per esempio, se si dispone di collegamenti che deve essere sempre posto al di sotto di nodi, effettuare le seguenti operazioni:

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

    Ora, quando si dipinge i collegamenti e i nodi, selezionare quanto segue (selettori a partire # di riferimento dell’elemento 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

    Ora, tutti i link sono sempre aggiunte strutturalmente prima di tutti i nodi. Così, il SVG mostra tutti i collegamenti di seguito tutti i nodi, non importa quanto spesso e in che ordine è possibile aggiungere o rimuovere elementi. Naturalmente, tutti gli elementi dello stesso tipo (cioè all’interno dello stesso contenitore) saranno ancora soggette all’ordine in cui sono stati aggiunti.

    • Mi sono imbattuto in questo con l’idea in testa che potevo spostare un elemento, quando il mio vero problema è stata l’organizzazione della mia elementi in gruppi. Se il “vero” problema è lo spostamento di singoli elementi, quindi il metodo di ordinamento descritto nella risposta precedente è un buon modo per andare.
    • Fantastico!!! Grazie, notan3xit, hai salvato la mia giornata! Solo per aggiungere un idea per gli altri – se avete bisogno di aggiungere elementi in ordine diverso dal desiderata visibilità ordine, è possibile creare gruppi nella giusta visibilità ordine (anche prima di aggiungere eventuali elementi di loro), e poi aggiungere gli elementi di questi gruppi in qualsiasi ordine.
    • Questo è l’approccio corretto. Ovvio, una volta che si pensa di esso.
  3. 21

    Dal SVG non ha Z-index, ma utilizzare l’ordine degli elementi del DOM, che si può portare in fronte da:

    this.parentNode.appendChild(this);

    È quindi possibile ad esempio utilizzare insertBefore rimettere in mouseout. Questo, tuttavia, richiede di essere in grado di indirizzare il fratello nodo elemento deve essere inserito prima.

    DEMO: dare un’occhiata a questo JSFiddle

    • Questa sarebbe una grande soluzione se non fosse per Firefox non riuscendo a eseguire il rendering di qualsiasi :hover stili. Anche io non credo che il ìnsert funzione alla fine sarebbe necessario per questo caso d’uso.
    • la tua soluzione funziona alla grande per me e porta l’elemento anteriore. Per quanto riguarda per portarla indietro, mi sto un po ‘ in pila e non so come scriverlo in modo corretto. Puoi, per favore, postare un esempio di come si può portare l’oggetto alla schiena. Grazie.
    • Certo, ho appena aggiornato la mia risposta. Ho capito d3 insert funzione probabilmente non è il modo migliore per andare, perché crea un nuovo elemento. Vogliamo solo per spostare un elemento esistente, quindi io uso insertBefore in questo esempio invece.
    • Ho provato ad usare questo metodo, ma purtroppo io non lavoro in IE11. Avete una soluzione per questo browser?
  4. 13

    SVG non z-index. Z-ordine è dettato dall’ordine in formato SVG DOM elementi nel loro contenitore.

    Per quanto ho potuto dire (e l’ho provato un paio di volte in passato), D3 non fornisce metodi per stacco e riattacco di un singolo elemento per portare in primo piano o quant’altro.

    C’è un .fine() metodo, che rimpasti i nodi corrispondono ordine in cui appaiono nella selezione. Nel tuo caso, è necessario portare un singolo elemento per il fronte. Quindi, tecnicamente, si potrebbe resort la selezione con l’elemento desiderato nella parte anteriore (o alla fine, non ricordo quale è più alto), e quindi chiamare order() su di esso.

    O, si potrebbe saltare d3 per questo compito e normale JS (jQuery o) per reinserire quel singolo elemento del DOM.

    • Faccio notare che ci sono problemi con la rimozione/inserimento di elementi all’interno di un mouseover gestore in alcuni browser, può portare a mouseout eventi non viene inviato correttamente, quindi vi consiglio di provare questo in tutti i browser per assicurarsi che funzioni come previsto.
    • Non alterare l’ordine, inoltre, modificare il layout del mio grafico a torta? Anche io sto scavando intorno jQuery per imparare a ri-inserire gli elementi.
    • Ho aggiornato la mia domanda con la mia soluzione che ho scelto, che non utilizzano il centro della domanda originale. Se vedi qualcosa di veramente brutto su questa soluzione sono commenti! Grazie per la comprensione sulla domanda originale.
    • Mi sembra un approccio ragionevole e, soprattutto, grazie al fatto che la sovrapposizione forma non hanno un riempimento. Se lo ha fatto, mi sarei aspettato di “rubare” l’evento del mouse momento in cui è apparso. E, penso che @ErikDahlström punto rimane lo stesso: questo potrebbe ancora essere un problema in alcuni browser (IE9 principalmente). Per extra “sicurezza” si potrebbe aggiungere .style('pointer-events', 'none') per la sovrapposizione del percorso. Purtroppo, questa proprietà non fare nulla in IE, ma ancora….
    • Ciao… domanda ottiene un sacco di punti di vista, e credo futurend la risposta di seguito è più utile di una miniera. Così, forse, pensare di cambiare il accettato di rispondere a futurend, di modo che appare più in alto.
  5. 4

    Ho implementato futurend la soluzione nel mio codice e ha funzionato, ma con il grande numero di elementi stavo usando, è stato molto lento. Ecco il metodo alternativo utilizzando jQuery che ha lavorato più veloce per la mia particolare visualizzazione. Essa si basa sul svgs che si desidera sulla cima di avere una classe in comune (nel mio esempio la classe è notato nel mio set di dati come d’.la chiave). Nel mio codice c’è un <g> con la classe “posizioni” che contiene tutte le SVGs sto ri-organizzazione.

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

    In modo che quando il mouse passa su un particolare punto di dati, il codice rileva tutti gli altri punti dati con SVG elementi del DOM con quella particolare classe. Poi si stacca e si re-inserisce il SVG DOM elementi associati a questi punti di dati.

  6. 4

    Voluto espandere su ciò che @notan3xit risposto piuttosto che scrivere un intero nuovo sistema per la risposta, ma non ho abbastanza reputazione).

    Un altro modo per risolvere l’elemento in ordine di problema è quello di utilizzare ‘inserisci’, piuttosto che ‘append’ quando disegno . Che modo i percorsi saranno sempre messi insieme prima di altri elementi svg(ciò presuppone che il codice già enter() per i collegamenti prima di enter() per gli altri elementi svg).

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

  7. 1

    Mi ci sono voluti secoli per trovare il modo di modificare l’ordine Z in SVG. Ho davvero bisogno in un contesto di d3.pennello con la descrizione dei comandi comportamento. Per avere due funzioni per lavorare bene insieme (http://wrobstory.github.io/2013/11/D3-brush-and-tooltip.html), è necessario che la d3.pennello per essere il primo in ordine Z (1 ° per essere disegnato sulla tela, poi coperto con il resto degli elementi SVG) e di catturare tutti gli eventi del mouse, non importa che cosa è su di esso (con maggiore Z indici).

    Più commenti nei forum dicono che si dovrebbe aggiungere la d3.spazzola prima del codice, allora il vostro SVG “disegno” del codice. Ma per me non era possibile in quanto ho caricato un esterno di file in formato SVG. Si può facilmente aggiungere il pennello in qualsiasi momento e modificare l’ordine Z tardi con:

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

    Nel contesto di una d3.spazzola di installazione si avrà un aspetto simile:

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

    Spero che questo aiuta!

  8. 0

    Versione 1

    In teoria, dovrebbe funzionare bene.

    Il codice CSS :

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

    Questo codice CSS verrà aggiunta una corsa per il percorso selezionato.

    Il codice JS :

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

    Questo codice JS primo rimuove il percorso da l’albero del DOM e poi aggiunge come l’ultimo figlio di suo padre. Questo assicura che il percorso è disegnato al di sopra di tutti gli altri figli di uno stesso padre.

    In pratica, questo codice funziona in Chrome, ma si rompe in altri browser. Ho provato in Firefox 20 sul mio Linux Mint macchina e non riuscivo a farlo funzionare. In qualche modo, Firefox non riesce a innescare il :hover stili e non ho trovato un modo per risolvere questo problema.


    Versione 2

    Così mi è venuta un’alternativa. Può essere un po ‘sporco’, ma almeno funziona e non necessita di iterare su tutti gli elementi (come alcune delle altre risposte).

    Il codice CSS :

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

    Invece di utilizzare il :hover pseudoselector, io uso un .hover classe

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

    Al passaggio del mouse, aggiungere il .hover di classe per il mio percorso. Su mouseout, lo rimuovi.
    Come nel primo caso, il codice rimuove anche il percorso da l’albero del DOM e poi aggiunge come l’ultimo figlio di suo padre.

  9. 0

    Si può Fare come questo Su Mouse Si può Tirare verso l’alto.

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

    Se Si vuole Spingere All’Indietro

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

Lascia un commento