d3.js - Qual è lo scopo di selectAll che restituisce un elemento non esistente [duplicato]

Aug 24 2020

Con d3.js ho il seguente codice semplice che aggiunge rettangoli in base al set di dati.

Non ho svg né rect, quindi lo aggiungo tramite .append ().

            let dataset = [ 1, 2, 3, 4, 5 ]
            let svg = d3.select("body")
                        .append("svg")
                        .attr /* width, height */

            svg.selectAll("rect")
               .data(dataset)
               .enter()
               .append("rect")
               .attr /* x, y, width, height, etc */

Funziona perfettamente.

Quello che ho domanda è selectAll("rect").

Perché è necessario? Se lo rimuovo come sotto, gli elementi rect andranno al di fuori dell'elemento body.

Ma l'append di svg non si supponeva da aggiungere alla fine dell'elemento di riferimento? (In questo caso svg?)

            svg.data(dataset)
               .enter()
               .append("rect")
               .attr /* x, y, width, height, etc */

So che selectAll restituisce il riferimento.

Se l'elemento non esiste, restituisce un riferimento speciale?

Risposte

1 HugoElhaj-Lahsen Aug 24 2020 at 02:15

D3 utilizza un paradigma per gli aggiornamenti chiamato data join . È ottimizzato per aggiornamenti incrementali dei dati, che si adattano all'utilizzo del grafico.

Per utilizzare il data join si opera sulle "selezioni". Invece di rappresentare ciò che vuoi fare, con le selezioni, rappresenti come vuoi che i dati si trasformino.

let dataset = [ {name: "a", value: 1}, {name: "b", value: 2}, {name: "c", value: 3} ]
let svg = d3.select("body")
  .append("svg")

const selection = svg.selectAll("rect")
  .data(dataset, d => d.name) // data join, each "rect" will correspond to an data
// second argument is a key function to uniquely identify each data

selection
  .enter()
  .append("rect")
  /* attr: x, y, height, width */

la selezione rappresenta l'unione dei dati. Qui, dici a D3 che stai effettuando una selezione. Al momento, tuttavia, non esiste nulla nel DOM, quindi otteniamo una selezione vuota.

Quando si definisce la selezione:

  • il DOM non ha nodi "rect"
  • ma ci sono dati

Guardandolo in un altro modo:

         data
 data  &nodes  nodes
       +-----+
+--------------------+
|      |     |       |
|  a:1 |     |       |
|  b:2 |     |       |
|  c:3 |     |       |
|      |     |       |
+--------------------+
       +-----+

La enterselezione conterrà i dati senza nodi. Quindi questi sono i dati che stai manipolando quando usi enter.

Supponiamo di modificare il set di dati ora. Come si aggiornano i nodi DOM?

dataset =  [ {name: "a", value: 9}, {name: "c", value: 3}, {name: "d", value: 4} ]
// note how we need to select rect to get the existing data.
// this is the "update" selection
const selection = svg.selectAll("rect")
  .data(dataset, d => d.name)
  .attr("x", d => d.value) 

// the "enter" selection
selection.enter()
  .append("rect")

// the "exit" selection
selection.exit().remove()

Quello che abbiamo ora sono tre selezioni. Se disegniamo di nuovo il nostro diagramma, ecco dove si trovano ora i nostri dati:

         data
 data  &nodes  nodes
       +-----+
+--------------------+
|      |     |       |
| d:4  | a:9 |  b:2  |
|      |     |       |
|      |     |       |
|      |     |       |
+--------------------+
       +-----+

Cosa succede nel momento in cui i dati si uniscono? I dati sono cambiati. Ora svg.selectAll("rect")prende una selezione già esistente: D3 salvato nel DOM. Questo è il motivo per cui devi passare un selettore CSS a selectAll. D3 utilizza quel selettore per trovare la selezione esistente, salvata nel DOM tramite un .__data__attributo su ogni nodo DOM.

Ora stiamo osservando nuovi dati e li confrontiamo con quelli che abbiamo nel DOM:

  • a ha ancora un dato e ha un nodo dal precedente data join. Sappiamo che a è ancora lì, ma il suo valore è cambiato poiché i nostri dati sono oggetti con {nome, valore}. Quindi è in "dati e nodi".
  • b ha un nodo nella dom, ma non esiste nei nuovi dati corrispondenti. È in "nodi".
  • d è un nuovo elemento: sono dati senza un nodo. Quindi va in "dati".

Poiché c non si aggiorna, non è in nessuna selezione .

Rinominiamo le cose nel nostro grafico:

 enter  update  exit
       +-----+
+--------------------+
|      |     |       |
| d:4  | a:9 |  b:2  |
|      |     |       |
|      |     |       |
|      |     |       |
+--------------------+
       +-----+

Quando si utilizza direttamente una selezione (la selezione dell'aggiornamento), la chiamata enter()o la chiamata exit(), si lavora sempre su una di queste tre selezioni. È molto più efficiente, soprattutto per dati di grandi dimensioni, gestire gli aggiornamenti solo quando necessario o non ricreare i nodi ogni volta, distruggendoli solo quando ne abbiamo bisogno.

Anche se utilizzi solo la enter()transizione, D3 sarà più efficiente assicurandosi che vengano creati solo nuovi elementi.

L'idea è che puoi inserire uno snippet che gestisce tutti e tre gli stati in qualcosa di simile a una render()funzione e chiamarlo tutto il tempo: quando generi per la prima volta il grafico e quando aggiorni i dati. D3 gestisce per te tutta la contabilità dati.